<!DOCTYPE HTML><html><head><meta charset="utf-8"></head><body><table><tr><td>https://blog.csdn.net/weixin_41475710</td><td>没有内容</td><td>没有内容</td></tr><tr><td>https://blog.csdn.net/weixin_41475710/article/details/82256535</td><td>#java    IO流的简单使用——教你开发一个属于自己的图片格式</td><td>


java IO流的简单使用——教你写一个属于自己的图片格式
说在前面：本次学习需要提前学会：java窗口的编写、画图板的制作；
1、	简单的I/O知识点：
（1）、保存一个数字或字符到指定文件的简单步骤：
第一步，建立一个OutputStream类以及DataOutputStream类：
OutputStream OutputStream类名= new FileOutputStream(文件路径以及文件名，没有则会自动创建);
DataOutputStream DataOutputStream类名 = new DataOutputStream(OutputStream对象名);

第二步，使用writeChar方法写入字符，使用writeInt方法写入整形数字：
   DataOutputStream对象名.writeChar('|');
   DataOutputStream对象名.writeInt(1);

第三步，刷新，讲流里面缓存的数据写入文件：
        DataOutputStream对象名.flush();

第四步，关闭文件：
   DataOutputStream对象名.close();


（2）、打开一个数字或字符到指定文件的简单步骤：
第一步，建立一个InputStream类以及DataInputStream类：
OutputStream InputStream类名= new FileInputStream(文件路径以及文件名，没有则会自动创建);

DataInputStream DataOutputStream类名 = new DataInputStream(OutputStream对象名);
第二步，使用writeChar方法写入字符，使用writeInt方法写入整形数字：
   DataInputStream对象名.readChar();
   DataInputStream对象名.readInt();

第三步，关闭文件：
   DataInStream对象名.close();


2、	写一个属于自己的图片格式
（1）、写一个窗口，添加两个按钮和两个文本框，两个按钮的文字分别为保存、打开，如下：
—DraFrame.java—

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextField;
public class DrawFrame {
	public static void main(String [] args){
		showUI();
	}	
	public static void showUI(){
	    JFrame jsf = new JFrame();
	    jsf.setSize(1000,900);
	    jsf.setTitle("画板");
	    jsf.setDefaultCloseOperation(3);   //设置退出程序方法
	    jsf.setLocationRelativeTo(null);   //设置居中显示
	 //   jsf.setResizable(false);
	    java.awt.FlowLayout jaf = new java.awt.FlowLayout();
	    jsf.setLayout(jaf);
	    JTextField jst_Name = new JTextField("请输入文件名");
	    jsf.add(jst_ Name);
	    JTextField jst_Path = new JTextField("请输入文件路径及文件名");
	    jsf.add(jst_Path);
	    JButton jsb_Sa = new JButton("保存");
	    jsf.add(jsb_Sa);
	    JButton jsb_Op = new JButton("打开");
	    jsf.add(jsb_Op);
	    jsf.setVisible(true);
    }
}

（2）、为窗口添加鼠标监听器，实现画直线功能：
—DraFrame.java—
import java.awt.Graphics;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextField;

public class DrawFrame {

	public static void main(String [] args){
		showUI();
	}	
	public static void showUI(){
	    JFrame jsf = new JFrame();
	    jsf.setSize(1000,900);
	    jsf.setTitle("画板");
	    jsf.setDefaultCloseOperation(3);   //设置退出程序方法
	    jsf.setLocationRelativeTo(null);   //设置居中显示
	 //   jsf.setResizable(false);
	    java.awt.FlowLayout jaf = new java.awt.FlowLayout();
	    jsf.setLayout(jaf);
	    JTextField jst_Name = new JTextField("请输入文件名");
	    jsf.add(jst_IfsX);
	    JTextField jst_Path = new JTextField("请输入路径及文件名");
	    jsf.add(jst_Path);
	    JButton jsb_Sa = new JButton("保存");
	    jsf.add(jsb_Sa);
	    JButton jsb_Op = new JButton("打开");
	    jsf.add(jsb_Op);
	    jsf.setVisible(true);
	    Graphics g = jsf.getGraphics();
	    DrawListener d = new DrawListener(g);
	    jsf.addMouseListener(d);
    }
}

— DrawListener.java—
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.Graphics;
public class DrawListener implements MouseListener {
	private int x1,x2,y1,y2;
	Graphics g;
	public DrawListener(Graphics g) {
		this.g = g;
	}
	@Override
	public void mouseClicked(MouseEvent e) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void mousePressed(MouseEvent e) {
		x1=e.getX();
	   	y1=e.getY();
	}

	@Override
	public void mouseReleased(MouseEvent e) {
		x2=e.getX();
	    y2=e.getY();
	    g.drawLine(x1, y1, x2, y2);
	}

	@Override
	public void mouseEntered(MouseEvent e) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void mouseExited(MouseEvent e) {
		// TODO Auto-generated method stub
	}
}

（3）、写一个类，在画线时将点的位置数据保存，同时在里面写一个画线的方法：
—PointInfo.java—

import java.awt.Graphics;

public class PointInfo {
	Graphics g;
    private int x1,y1,x2,y2;
    PointInfo(int x1, int y1, int x2, int y2){
    	this.x1=x1;
    	this.y1=y1;
    	this.x2=x2;
    	this.y2=y2;
    }
    public void draw(){
    	g.drawLine(x1, y1, x2, y2);
    }
}

（4）、在监听器类里写一个PointInfo：的对象数组，在画线时将各点数据保存：
—DrawListener.java—

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.Graphics;
public class DrawListener implements MouseListener,ActionListener {
	private int count = 0;
	PointInfo[] point = new PointInfo[100];
	private int x1,x2,y1,y2;
	Graphics g;
	
	public DrawListener(Graphics g) {
		this.g = g;
	}
	
	public void actionPerformed(ActionEvent e) {
		// TODO Auto-generated method stub
		
	}
	
	public void mouseClicked(MouseEvent e) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void mousePressed(MouseEvent e) {
		x1=e.getX();
	   	y1=e.getY();
	}

	@Override
	public void mouseReleased(MouseEvent e) {
		x2=e.getX();
	    y2=e.getY();
	    g.drawLine(x1, y1, x2, y2);
	    PointInfo p = new PointInfo(x1, y1, x2, y2);
	    point[count++] = p;
	}

	@Override
	public void mouseEntered(MouseEvent e) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void mouseExited(MouseEvent e) {
		// TODO Auto-generated method stub
		
	}
}

（4）、为保存按钮添加事件监听器，实现保存功能，为打开按钮添加事件监听器，实现打开功能：

---DrawFrame.java---

import java.awt.Graphics;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextField;

public class DrawFrame {

	public static void main(String [] args){
		showUI();
	}	
	public static void showUI(){
	    JFrame jsf = new JFrame();
	    jsf.setSize(1000,900);
	    jsf.setTitle("画板");
	    jsf.setDefaultCloseOperation(3);   //设置退出程序方法
	    jsf.setLocationRelativeTo(null);   //设置居中显示
	 //   jsf.setResizable(false);
	    java.awt.FlowLayout jaf = new java.awt.FlowLayout();
	    jsf.setLayout(jaf);
	    JTextField jst_Name = new JTextField("请输入文件名");
	    jsf.add(jst_Name);
	    JTextField jst_Path = new JTextField("请输入路径及文件名");
	    jsf.add(jst_Path);
	    JButton jsb_Sa = new JButton("保存");
	    jsf.add(jsb_Sa);
	    JButton jsb_Op = new JButton("打开");
	    jsf.add(jsb_Op);
	    jsf.setVisible(true);
	    Graphics g = jsf.getGraphics();
	    DrawListener d = new DrawListener(g,jst_Name,jst_Path);
	    jsb_Sa.addActionListener(d);
	    jsb_Op.addActionListener(d);
	    jsf.addMouseListener(d);
    }
}

—DrawListener.java—

import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import javax.swing.JTextField;

public class DrawListener implements MouseListener,ActionListener {
	private int count = 0;
	PointInfo[] point = new PointInfo[100];
	private int x1,x2,y1,y2;
	Graphics g;
	String Bt;
	private JTextField TxN;
	private JTextField TxP;
	
	public DrawListener(Graphics g,JTextField t1,JTextField t2) {
		this.g = g;
		TxN = t1;
		TxP = t2;
	}
	
	public void WriteFile() throws IOException {
		if (Bt.equals("保存")) {
			String fileName = TxN.getText();
			File f = new File("D://" + fileName + ".ant");//后面的点后的即为文件格式，可以随意设置
			OutputStream out = new FileOutputStream(f);
			DataOutputStream dOut = new DataOutputStream(out);
			for (int i = 0; i < point.length; i++) {
				//将个坐标的位置存入，用'|'隔开
				dOut.writeChar('|');  //写一个斜杠
				dOut.writeInt(point[i].x1); //写x1的坐标
				dOut.writeChar('|'); 
				dOut.writeInt(point[i].y1);
				dOut.writeChar('|'); 
				dOut.writeInt(point[i].x2);
				dOut.writeChar('|'); 
				dOut.writeInt(point[i].y2);
				out.flush();
				dOut.flush();
			}
			out.close();
			dOut.close();
		}
	}
	public void ReadFile() throws IOException {
		if (Bt.equals("打开")) {
			count = 0 ;
			String filePath = TxP.getText();
			InputStream in = new FileInputStream(filePath);
			DataInputStream dIn = new DataInputStream(in);
			while (true) {
			//	dIn.readChar();
				if (dIn.readChar() == -1) {
					System.out.println("读取完毕");
					break;
				}
				int x1 = dIn.readInt();
				dIn.readChar();
				int y1 = dIn.readInt();
				dIn.readChar();
				int x2 = dIn.readInt();
				dIn.readChar();
				int y2 = dIn.readInt();
				PointInfo p =new PointInfo(x1, y1, x2, y2);
				p.draw(g);
			}
			in.close();
			dIn.close();
          
			// System.out.println(a);
			// dIn.
		}
	}
	
	public void actionPerformed(ActionEvent e) {
		// TODO Auto-generated method stub
		Bt = e.getActionCommand();
		try {
			ReadFile();
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		try {
			WriteFile();
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
	}
	
	public void mouseClicked(MouseEvent e) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void mousePressed(MouseEvent e) {
		x1=e.getX();
	   	y1=e.getY();
	}

	@Override
	public void mouseReleased(MouseEvent e) {
		x2=e.getX();
	    y2=e.getY();
	    g.drawLine(x1, y1, x2, y2);
	    PointInfo p = new PointInfo(x1, y1, x2, y2);
	    point[count++] = p;
	}

	@Override
	public void mouseEntered(MouseEvent e) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void mouseExited(MouseEvent e) {
		// TODO Auto-generated method stub
		
	}
}

—PointInfo.java—

import java.awt.Graphics;

public class PointInfo {
    int x1,y1,x2,y2;
    PointInfo(int x1, int y1, int x2, int y2){
    	this.x1=x1;
    	this.y1=y1;
    	this.x2=x2;
    	this.y2=y2;
    }
    public void draw(Graphics g){
    	g.drawLine(x1, y1, x2, y2);
    }
}

这样即可实现：在第一个文本框里输入文件名点保存，指定路径便会出点指定文件：
在第二个文本框点输入路径及文件名，点打开即可看到响应图形：
更多图像保存的方法和上面类似，按实际情况稍作更改和设计读写规则即可。
</td></tr><tr><td>https://blog.csdn.net/weixin_41475710/article/details/82757621</td><td>#java    ArrayList<T>、线程、Image对象示例——飞机大战开发如此简单</td><td>


java    ArrayList、线程、Image对象示例——飞机大战开发如此简单
学习本文需要先准备的知识：窗体编写、窗体元素添加、鼠标监听器的使用、线程的使用（可以查看我的上一篇博客：“java线程 、键盘监听器——教你写自己的球球大作战”）
1、	前期准备（知识点讲解）
（1）、java ArrayList
a、什么是ArrayList
ArrayList可以说是一个动态数组，它的容量可以随时调整。
b、怎样使用ArrayList
定义语法：
ArrayList<类名> ArrayList名= new ArrayList<类名>();  //<>里面必须是除了基本数据类型外的自定义数据类型（类）

给ArrayList<>添加内容：ArrayList对象名.add(对象名);
从ArrayList<>删除内容：
ArrayList对象名.remove(i); //删除第i个ArrayList成员
ArrayList对象名.remove(对象名); //如果ArrayList中存在此对象，则删除
ArrayList对象名.removeAll(ArrayList<T>对象名); //移除所有ArrayList<T>对象

例：
ArrayList<StoreThings> things = new ArrayList<StoreThings>();
StoreThings sts = new StoreThings(image, (jsf.getWidth() - 90) / 2, jsf.getHeight() - 90, 90, 90, 0, g, "m_p",jsf, things);
things.add(sts);

（2）、Image对象的使用
a、	Image对象的用途：用于储存图片
b、	Image对象的用法
Image image对象名 = new ImageIcon(this.getClass().getResource("图片名，包括后缀名")).getImage();  //图片必须和代码文件放在同一根目录下

c、	将Image在窗体里绘制出来
 Graphics对象名. drawImage(Image对象名, 图片左上角X坐标, 图片左上角Y坐标, 图片的宽度（可选）, 图片的高度（可选）, 需要绘制图片的窗体对象名);

例：
Image BackGround = new ImageIcon(this.getClass().getResource("BackGround.jpg")).getImage();
g.drawImage(BackGround, 100, 0, jsf.getWidth(), jsf.getHeight(), jsf);

2、	开发飞机大战
提示：可以从网上寻找飞机图片、子弹图片以及背景图片
（1）、游戏规则
玩家控制一架飞机，窗口最上方会不断地出现敌方飞机，同时我方飞机和地方飞机都会自动发子弹，我方的飞机子弹速度稍微快一些。当我方的子弹碰到敌机后敌机消失，敌方的子弹碰到我方飞机，我方死亡，结束游戏。
（2）、代码思路
a、背景、飞机和子弹的绘制：
写一个类储存飞机和子弹的信息，要能够保存飞机和子弹的位置、大小、对应的图片、移动速度等信息，当然一定要添加一个属性区分地方和我方、飞机和子弹，同时还需要有将该图片绘制出来的方法。
import java.awt.Graphics;
import java.awt.Image;
import java.util.ArrayList;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JOptionPane;

public class StoreThings {
	private int x, y, width, heigth;
	private Graphics g;
	private Image image;
	private String type;
	private JFrame jsf;
	private int speed = 0;
	private ArrayList<StoreThings> things;

	public StoreThings(Image image, int x, int y, int width, int heigth, int speed, Graphics g, String type, JFrame jsf,
			ArrayList<StoreThings> things) {
		this.image = image;
		this.x = x;
		this.y = y;
		this.width = width;
		this.heigth = heigth;
		this.speed = speed;
		this.g = g;
		this.type = type;
		this.jsf = jsf;
		this.things = things;
	}

	public void setX(int x) {
		this.x = x;
	}

	public int getWidth() {
		return width;
	}

	public void setY(int y) {
		this.y = y;
	}

	public int getX() {
		return x;
	}

	public int getY() {
		return y;
	}

	public String getType() {
		return type;
	}

	public int getSpeed() {
		return speed;
	}

	public void setSpeed(int speed) {
		this.speed = speed;
	}

	public void showThings() {    //绘制图片的方法
		g.drawImage(image, x, y, width, heigth, jsf);
	}
}

定义一个上述类的ArrayList对象，用来保存出现的各个飞机和子弹；
ArrayList<StoreThings> things = new ArrayList<StoreThings>();

写一个专门负责绘制的线程，里面的内容包括：绘制背景，然后执行ArrayList对象里每个成员的绘制方法，最后将每一个对象的X坐标加上他的速度，然后在此等待一段时间。以上这些内容可以放在一个while(true)循环里面。
while (true) {
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			if (pause) {
				g.drawImage(BackGround, 100, 0, jsf.getWidth(), jsf.getHeight(), jsf);
				for (int i = 0; i < things.size(); i++) {
					StoreThings temp = things.get(i);
					temp.showThings();
					temp.setY(temp.getY()+temp.getSpeed());
				}
			}
		}

b、敌机的出现：
敌机的出现的位置纵坐标为0，横坐标可以是窗体0到窗体宽度范围的随机数，可以写一个继承了线程的对象用于生成敌机（定时创建敌机对象，并添加到一个ArrayList对象里面）：
public void run() {
		while (start) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			if (pause) {
				x = 100 + rand.nextInt(jsf.getWidth());
				Image image = new ImageIcon(this.getClass().getResource("EnemyPlane.png")).getImage();
				StoreThings st = new StoreThings(image, x, y, 80, 80, 10, g, "e_p", jsf, things);
				things.add(st);
			}
		}
	}

c、我方飞机的出现：可在监听器类的构造函数里写，建立一个Image对象，用Graphics的drawImage方法绘制出来。
public PlaneListener(Graphics g, JFrame jsf) {
		this.g = g;
		this.jsf = jsf;
		st = new ShowThread(g, jsf, things);
		mpt = new MyPlaneThread(jsf, things, g);
		et = new EnemyThread(g, jsf, things);
Image image = new ImageIcon(this.getClass().getResource("MyPlane.png")).getImage();  //我方飞机的图片
			StoreThings sts = new StoreThings(image, (jsf.getWidth() - 90) / 2, jsf.getHeight() - 90, 90, 90, 0, g, "m_p", jsf, things);   //将我方飞机绘制出来
	}

c、子弹的出现：
创建子弹对象，位置在各个飞机的中间位置，同时注意速度属性应该要比飞机快一点，同时，我方子弹的速度和地方子弹的速度方向相反，可以单独放在一个线程里面。
try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			if (pause) {
				x = 100 + rand.nextInt(jsf.getWidth());
				Image image = new ImageIcon(this.getClass().getResource("EnemyPlane.png")).getImage();
				StoreThings st = new StoreThings(image, x, y, 80, 80, 10, g, "e_p", jsf, things);
				things.add(st);
				image = new ImageIcon(this.getClass().getResource("bullet1.png")).getImage();
				for (int i = 0; i < things.size(); i++) {
					StoreThings temp = things.get(i);
					if (temp.getType().equals("e_p")) {
						StoreThings Bullet = new StoreThings(image, temp.getX() + 28, temp.getY() + 40, 25, 25, 20, g,
								"e_b", jsf, things);
						things.add(Bullet);
					}
				}
			}

d、被子弹击中后
可以用飞机与子弹的距离和宽度等信息判断二者是否相碰，当我方飞机碰到敌方子弹时，或者地方飞机碰到我方子弹时应当爆炸消失（从ArrayList里面移除该对象，此方法建议放在储存这些信息的类里的绘制方法里执行，这样可以保证能及时判断二者的位置）：
public void crash(StoreThings st) {
		if (st.getType().equals("e_p")) {
			for (int i = 0; i < things.size(); i++) {
				StoreThings temp = things.get(i);
				if (temp.getType().equals("m_b")) {
					double cx = temp.getX() - st.getX();
					double cy = temp.getY() - st.getY();
					double cw = temp.getWidth() + st.getWidth();
					if (Math.sqrt(cx * cx + cy * cy) <= cw / 2) {
						things.remove(st);
					}
				}
			}
		}

		if (st.getType().equals("m_p")) {
			for (int i = 0; i < things.size(); i++) {
				StoreThings temp = things.get(i);
				if (temp.getType().equals("e_b")) {
					double cx = temp.getX() - st.getX();
					double cy = temp.getY() - st.getY();
					double cw = temp.getWidth() + st.getWidth()-40;  //经测试有偏差，此处减去40以减小判断误差
					if (Math.sqrt(cx * cx + cy * cy) <= cw / 2) {
						things.remove(st);
						JOptionPane.showMessageDialog(null, "玩家死亡，游戏结束！");
						System.exit(0);
					}
				}
			}
		}
	}

e、暂停和继续游戏的实现
在界面添加继续游戏和暂停游戏按钮，为其添加事件监听器。在每个线程while循环里加一个if(boolean类型数据)的判断，为这个boolean类型数据写一个set方法，点击暂停时将其改成false，点击继续将其改成true。
注意：while和if之间最好放上几句代码，不然可能无法实现暂停功能，可以把延时的语句（Thread.sleep()）放在此处。
  case "暂停游戏":
		st.setPause(false);
		mpt.setPause(false);
		et.setPause(false);
		break;
	case "继续游戏":
		st.setPause(true);
		mpt.setPause(true);
		et.setPause(true);
		break;

3、全部的代码
–ShowFrame.java–
package com.antony.PlaneBattle0916;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class ShowFrame {
	public static void main(String[] args) {
		showUI();
	}

	public static void showUI() {
		JFrame jsf = new JFrame();
		jsf.setDefaultCloseOperation(3);
		jsf.setSize(700, 910);
		jsf.setLocationRelativeTo(null);
		BorderLayout boder = new BorderLayout();
		FlowLayout flow = new FlowLayout();
		jsf.setLayout(boder);
		JPanel jp = new JPanel();
		jp.setLayout(flow);
		Dimension d = new Dimension(100, 910);
		jp.setPreferredSize(d);
		jp.setBackground(Color.GREEN);
		JLabel jsl_place = new JLabel();
		Dimension dl = new Dimension(100,300);
		jsl_place.setPreferredSize(dl);
		Dimension db = new Dimension(80, 26);
		Font fb = new Font("宋体",Font.PLAIN, 11);
		JButton jsb_start = new JButton("开始游戏");
		jsb_start.setFont(fb);
		jsb_start.setPreferredSize(db);
		JButton jsb_pause = new JButton("暂停游戏");
		jsb_pause.setFont(fb);
		jsb_pause.setPreferredSize(db);
		JButton jsb_continue = new JButton("继续游戏");
		jsb_continue.setFont(fb);
		jsb_continue.setPreferredSize(db);
		JButton jsb_exit = new JButton("停止游戏");
		jsb_exit.setFont(fb);
		jsb_exit.setPreferredSize(db);
		jp.add(jsl_place);
		jp.add(jsb_start);
		jp.add(jsb_pause);
		jp.add(jsb_continue);
		jp.add(jsb_exit);
		jsf.getContentPane().add("West", jp);
		jsf.setVisible(true);
		Graphics g = jsf.getGraphics();
		PlaneListener pl = new PlaneListener(g, jsf);
		jsf.addMouseListener(pl);
		jsf.addMouseMotionListener(pl);
		jsb_start.addActionListener(pl);
		jsb_pause.addActionListener(pl);
		jsb_continue.addActionListener(pl);
		jsb_exit.addActionListener(pl);
	}
}

–PlaneListener.java–
package com.antony.PlaneBattle0916;

import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;

import javax.swing.ImageIcon;
import javax.swing.JFrame;

public class PlaneListener extends MouseAdapter implements ActionListener {
	private Graphics g;
	private JFrame jsf;
	private ArrayList<StoreThings> things = new ArrayList<StoreThings>();
	private ShowThread st = new ShowThread(g, jsf, things);
	private MyPlaneThread mpt = new MyPlaneThread(jsf, things, g);
	private EnemyThread et = new EnemyThread(g, jsf, things);

	public PlaneListener(Graphics g, JFrame jsf) {
		this.g = g;
		this.jsf = jsf;
		st = new ShowThread(g, jsf, things);
		mpt = new MyPlaneThread(jsf, things, g);
		et = new EnemyThread(g, jsf, things);
		Image BackGround = new ImageIcon(this.getClass().getResource("BackGround.jpg")).getImage();
		g.drawImage(BackGround, 100, 0, jsf.getWidth(), jsf.getHeight(), jsf);
	}

	public void mouseMoved(MouseEvent e) {
		int x = e.getX();
		int y = e.getY();
		for (int i = 0; i < things.size(); i++) {
			StoreThings temp = things.get(i);
			if (x < 145) {
				x = 145;
			}
			if (temp.getType().equals("m_p")) {
				temp.setX(x - 45);
				temp.setY(y - 45);
			}
		}
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		// TODO Auto-generated method stub
		String BT = e.getActionCommand();
		switch (BT) {
		case "开始游戏":
			if (!(mpt.isAlive() || st.isAlive() || st.isAlive())) {
				mpt.start();
				et.start();
				st.start();
			}
			things.removeAll(things);
			Image image = new ImageIcon(this.getClass().getResource("MyPlane.png")).getImage();
			StoreThings sts = new StoreThings(image, (jsf.getWidth() - 90) / 2, jsf.getHeight() - 90, 90, 90, 0, g, "m_p",
					jsf, things);
			things.add(sts);
			break;
		case "暂停游戏":
			st.setPause(false);
			mpt.setPause(false);
			et.setPause(false);
			break;
		case "继续游戏":
			st.setPause(true);
			mpt.setPause(true);
			et.setPause(true);
			break;
		case "停止游戏":
			System.exit(0);
			break;
		}
	}
}

–StoreThings.java–
package com.antony.PlaneBattle0916;

import java.awt.Graphics;
import java.awt.Image;
import java.util.ArrayList;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JOptionPane;

public class StoreThings {
	private int x, y, width, heigth;
	private Graphics g;
	private Image image;
	private String type;
	private JFrame jsf;
	private int speed = 0;
	private ArrayList<StoreThings> things;

	public StoreThings(Image image, int x, int y, int width, int heigth, int speed, Graphics g, String type, JFrame jsf,
			ArrayList<StoreThings> things) {
		this.image = image;
		this.x = x;
		this.y = y;
		this.width = width;
		this.heigth = heigth;
		this.speed = speed;
		this.g = g;
		this.type = type;
		this.jsf = jsf;
		this.things = things;
	}

	public void crash(StoreThings st) {
		if (st.getType().equals("e_p")) {
			for (int i = 0; i < things.size(); i++) {
				StoreThings temp = things.get(i);
				if (temp.getType().equals("m_b")) {
					double cx = temp.getX() - st.getX();
					double cy = temp.getY() - st.getY();
					double cw = temp.getWidth() + st.getWidth();
					if (Math.sqrt(cx * cx + cy * cy) <= cw / 2) {
						things.remove(st);
					}
				}
			}
		}

		if (st.getType().equals("m_p")) {
			for (int i = 0; i < things.size(); i++) {
				StoreThings temp = things.get(i);
				if (temp.getType().equals("e_b")) {
					double cx = temp.getX() - st.getX();
					double cy = temp.getY() - st.getY();
					double cw = temp.getWidth() + st.getWidth()-40;
					if (Math.sqrt(cx * cx + cy * cy) <= cw / 2) {
						things.remove(st);
						JOptionPane.showMessageDialog(null, "玩家死亡，游戏结束！");
						System.exit(0);
					}
				}
			}
		}
	}

	public void setX(int x) {
		this.x = x;
	}

	public int getWidth() {
		return width;
	}

	public void setY(int y) {
		this.y = y;
	}

	public int getX() {
		return x;
	}

	public int getY() {
		return y;
	}

	public String getType() {
		return type;
	}

	public int getSpeed() {
		return speed;
	}

	public void setSpeed(int speed) {
		this.speed = speed;
	}

	public void showThings() {
		for (int i = 0; i < things.size(); i++) {
			StoreThings temp = things.get(i);
			crash(temp);
		}
		g.drawImage(image, x, y, width, heigth, jsf);
	}
}

–EnemyThread.java–
package com.antony.PlaneBattle0916;

import java.awt.Graphics;
import java.awt.Image;
import java.util.ArrayList;
import java.util.Random;

import javax.swing.ImageIcon;
import javax.swing.JFrame;

public class EnemyThread extends Thread {
	private int x, y = 0;
	private Graphics g;
	private JFrame jsf;
	private ArrayList<StoreThings> things;
	private Random rand = new Random();
	private boolean start = true;
	private boolean pause = true;

	public EnemyThread(Graphics g, JFrame jsf, ArrayList<StoreThings> things) {
		this.g = g;
		this.jsf = jsf;
		this.things = things;
	}
	
	public void setStart(boolean start){
		this.start = start;
	}
	
	public void setPause(boolean pause){
		this.pause =pause;
	}

	public void run() {
		while (start) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			if (pause) {
				x = 100 + rand.nextInt(jsf.getWidth());
				Image image = new ImageIcon(this.getClass().getResource("EnemyPlane.png")).getImage();
				StoreThings st = new StoreThings(image, x, y, 80, 80, 10, g, "e_p", jsf, things);
				things.add(st);
				image = new ImageIcon(this.getClass().getResource("bullet1.png")).getImage();
				for (int i = 0; i < things.size(); i++) {
					StoreThings temp = things.get(i);
					if (temp.getType().equals("e_p")) {
						StoreThings Bullet = new StoreThings(image, temp.getX() + 28, temp.getY() + 40, 25, 25, 20, g,
								"e_b", jsf, things);
						things.add(Bullet);
					}
				}
			}
		}
	}
}

–MyPlaneThread.java–
package com.antony.PlaneBattle0916;

import java.awt.Graphics;
import java.awt.Image;
import java.util.ArrayList;

import javax.swing.ImageIcon;
import javax.swing.JFrame;

public class MyPlaneThread extends Thread {
	private boolean start = true;
	private JFrame jsf;
	private ArrayList<StoreThings> things;
	private Image image;
	private Graphics g;
	private boolean pause = true;

	public MyPlaneThread(JFrame jsf, ArrayList<StoreThings> things, Graphics g) {
		this.jsf = jsf;
		this.things = things;
		this.g = g;
		image = new ImageIcon(this.getClass().getResource("bullet2.png")).getImage();
	}
	
	public void setStart(boolean start){
		this.start = start;
	}
	
	public void setPause(boolean pause){
		this.pause =pause;
	}

	public void run() {
		while (start) {
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			if (pause) {
				for (int i = 0; i < things.size(); i++) {
					StoreThings temp = things.get(i);
					if (temp.getType().equals("m_p")) {
						StoreThings Bullet = new StoreThings(image, temp.getX() + 31, temp.getY(), 30, 30, -40, g,
								"m_b", jsf, things);
						things.add(Bullet);
					}
				}
			}
		}
	}
}

–ShowThread.java–
package com.antony.PlaneBattle0916;

import java.awt.Graphics;
import java.awt.Image;
import java.util.ArrayList;

import javax.swing.ImageIcon;
import javax.swing.JFrame;

public class ShowThread extends Thread {
	private Graphics g;
	private Image BackGround = new ImageIcon(this.getClass().getResource("BackGround.jpg")).getImage();
	private JFrame jsf;
	private boolean start = true;
	private boolean pause = true;
	private ArrayList<StoreThings> things;
	
	public ShowThread(Graphics g, JFrame jsf, ArrayList<StoreThings> things) {
		this.g = g;
		this.jsf = jsf;
		this.things = things;
	}
	
	public void setStart(boolean start){
		this.start = start;
	}
	
	public void setPause(boolean pause){
		this.pause =pause;
	}

	public void run() {
		while (start) {
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			if (pause) {
				g.drawImage(BackGround, 100, 0, jsf.getWidth(), jsf.getHeight(), jsf);
				for (int i = 0; i < things.size(); i++) {
					StoreThings temp = things.get(i);
					temp.showThings();
					temp.setY(temp.getY()+temp.getSpeed());
				}
			}
		}
	}
}

</td></tr><tr><td>https://blog.csdn.net/weixin_41475710/article/details/82876573</td><td>#java    队列与泛型——动态数组的实现原理</td><td>


java    队列与泛型——动态数组的实现原理
学习本文需要先准备的知识点：java数组
1、思路推理
众所周知，在很多编程语言中，数组的大小一旦确定，在定义后是不可以改变的。如果数组储存满了之后还想往数组里面储存元素，肯定不能接着用同一个数组。所以需要重新定义一个比原数组要大的数组，把原数组里的数据重新储存到新数组，再把新数据储存进去。代码如下：
public class MyArray {
       //声明初始数组
       private  Object[] array;
       //用于储存数组的大小
       private  int size;
       //在对象被创建时创建数组，初始大小为0
       public MyArray() {
              array = new Object[0];
       }
       public boolean add(Object newPart) {
              // 创建一个新数组
              Object[] newArray = new Object[size + 1];
              // 将原有的数组的内容赋给新数组
                  for (int i = 0; i < array.length; i++) {
                     newArray[i] = array[i]; 
              }
              //添加的数据写入新数组
              newArray[size++]  = newPart;
             //将数组array指向newArray
              array = newArray;
              return true;
       }

}

上述代码实现的MyArray对象可以实现储存任意个元素，但有两个明显的缺陷：
缺陷一：可以在同一个MyArray对象里面储存任意的对象。这貌似很不错，但在实际使用中，很多地方都只需要储存同一类的对象，用以上的方法无法判断所储存的元素是否是同一个类的对象。这就需要用到泛型。
泛型允许我们使用E、K、V等字母（）来代替不确定的数据类型（注意：E、K、V等字母不属于数据类型，只是一个符号，不能用在数据类型需要确定的地方；这里的数据类型不包括基本数据类型，如果需要用到基本数据类型，则用基本数据类型对应的类来替代）。具体方法如下：
首先在类名后加“<>”，尖括号里用E代替用户需要使用的数据类型：
public class MyArray<E> {……………}

然后将类的成员里面，不确定的数据类型都用E替代，替代后完整代码如下：
public class MyArray<E> {
   //声明初始数组
   private Object[] array;
   //用于储存数组的大小
   private int size;
   //在对象被创建时创建数组，初始大小为0
   public MyArray() {
          array= new Object[0];
   }
   public boolean add(E newPart) {        
          //创建一个新数组
          Object[] newArray = new Object[size + 1];
          //将原有的数组的内容赋给新数组
          for(int i = 0; i < array.length; i++) {
                 newArray[i]= array[i];
          }
          //添加的数据写入新数组
          newArray[size++]= newPart;
          //将数组array指向newArray
          array= newArray;
          return true;
   }
}

缺陷二：每储存一个数据就需要新定义一个数组，效率低下。我们可以给MyArray添加一个储存比例，让其初始化的时候有一定的大小（增长比例），等数据储存过量时再创建新数组，而且这个数组的大小要比原数组增加一定的大小（增长比例），而不是只增加一。另外，在存入数据时，需要先判断原数组里面是否有空值，有的话就将数据直接存入，没有再创建新数组。完整代码如下：
public class MyArray<E> {
       private Object[] array;
       private int size;
       private int addScale = 5; // 设置增长比例，这里暂时设置为5
       public  MyArray() {
        //初始大小由增长比例来确定
              array = new Object[addScale];
       }
       public boolean add(E newPart) {
             //检查之前数组是否有空的元素，如果有，则将添加内容补充到空元素
              for (int i = 0; i < size; i++) {
                     if  (array[i] == null) {
                            array[i]  = newPart;
                            return true;
                     }
              }
              //创建一个新数组
             // 按增长比例定义新数组，以提高效率
              Object[] newArray = new Object[size + addScale];
              //将原有的数组的内容赋给新数组
              for (int i = 0; i < array.length; i++) {
                     newArray[i]  = array[i];
              }
              //添加的数据写入新数组
              newArray[size]= newPart;
              size = size + addScale;
              array= newArray;
              return true;
       }

2、其它方法的实现
（1）、在指定的位置添加新元素：
        // 在索引为index的地方插入newPart
               public boolean add(int index, E newPart) {
                  if(index < 0 || index > size)
                         return false;
                  if(index == size) {
                         add(newPart);
                         return true;
                  }
                  add((E) null);
                  for (int i = size - 1; i > index; i--) {
                         array[i]= array[i - 1];
                  }
                  array[index] = newPart;
                  return true;
           }

（2）、往数组里添加数组：
    // 往数组里添加数组
 public void add(MyArray<E> e) {
              for(int i = 0; i < e.size(); i++) {
                   if(e.get(i) != null) {
                             //利用已经定义的add函数
                            add((E)e.get(i));
                     }
              }
       }

（3）、在指定位置插入某个元素
// 在索引为index的地方插入newPart
       public boolean add(int index, E newPart) {
              if(index < 0 || index > size)
                     return false;
              if(index == size) {
                     add(newPart);
                     return true;
              }
              add((E)null);
              for (int i = size - 1; i > index; i--) {
                     array[i]= array[i - 1];
              }
              array[index] = newPart;
              return true;
       }

（4）、在指定位置插入数组：
// 将数组插入索引为index的元素前

   public int add(int index, MyArray<E> e) {
          int count = 0;
          if(index < 0 || index > size)
                 return count;
          for(int i = 0; i < e.size(); i++) {
         //利用（3）里的函数来实现插入单个元素
                 add(index+i,(E)e.get(i));
                 count++;
          }
          return count;
   }

（5）、将指定位置的元素替换成新元素：
// 将索引为index的数据替换为newPart
   public boolean update(int index, E newPart) {

          if(index < 0 || index >= size)
                 return false;
          else{
                 array[index]= newPart;
                 return true;
          }
   }

（6）、将指定的第一个元素替换成新元素：
// 将第一个内容为e的元素替换为newPart

       public boolean update(E e, E newPart) {

              for (int i = 0; i < size; i++) {

                     if(array[i] == e) {
                            array[i]    = newPart;
                            return true;
                     }
              }
              return false;
       }

（7）、将指定的所有元素替换成新元素：
  // 将所有内容为e的元素替换为newPart，返回值为替换的元素个数

   public int updateAll(E e, E newPart) {
          int count = 0;
          for(int i = 0; i < size; i++) {
                 if(array[i] == e) {
                        array[i] = newPart;
                        count++;
                 }
          }
          return count;
   }

（8）、移除第一个指定的元素：
 // 如果元素deletePart存在，则删除第一个deletePart元素
   public boolean remove(E deletePart) {
          for(int i = 0; i < size; i++) {
                 if(get(i) == deletePart) {
                        array[i] = null;
                        complement(i);
                        return true;
                 }
          }
          return false;
   }

 // 将所有靠后的数据向前面为空的索引替换补全
   public void complement(int index) {
          for(int j = index; j < size - 1; j++) {
                 array[j] = array[j + 1];
          }
          size--;
   }

（9）、执行指定索引的元素：
// 删除索引为index的元素，返回值为删除的元素
   public E remove(int index) {
          if(index < 0 || index > size) {
                 return null;
          }
          E removePart = (E) array[index];
          array[index] = null;
          complement(index);
          return removePart;
   }

// 将所有靠后的数据向前面为空的索引替换补全
   public void complement(int index) {
          for(int j = index; j < size - 1; j++) {
                 array[j] = array[j + 1];
          }
          size--;
   }

（10）、移除所有指定的元素：
    // 删除所有元素和deletePart相同的内容，返回值为更改的元素数量
   public int removaAll(E deletePart) {
          int count = 0;
          for(int i = 0; i < size; i++) {
                 if(array[i] == deletePart) {
                        array[i] = null;
                        complement(i);
                        i--;
                        count++;
                 }
          }
          return count;
   }



// 将所有靠后的数据向前面为空的索引替换补全
   public void complement(int index) {
          for(int j = index; j < size - 1; j++) {
                 array[j] = array[j + 1];
          }
          size--;
   }

（11）、获取指定索引的元素：
// 获取索引为index的元素

   public Object get(int index) {

          return array[index];
   }

（12）、获取数组的大小
  // 获取数组的大小

   public int size() {
          return size;
   }

</td></tr><tr><td>https://blog.csdn.net/weixin_41475710/article/details/82724876</td><td>#java    线程 、键盘监听器——教你写自己的球球大作战</td><td>


java    线程 、键盘监听器——教你写自己的球球大作战
学习本文需要先准备的知识：窗体编写、窗体元素添加、窗体绘制填充圆
1、	前期准备（知识点讲解）
（1）、java线程
a、为什么要用线程
案例：想要写一个会移动的小球，我们可以采取这样的方法：写一个while(true)循环，在里面不断地给圆的横纵坐标自加，然后把这个球重新画出来，之前画过球的地方用白色的球填充，同时循环里加一个延时的代码就行，如下：
while (true) {
			try {
				Thread.sleep(30);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			double addX = -2+rand.nextInt(4000)*0.001;
			double addY = -2+rand.nextInt(4000)*0.001;
			g.setColor(Color.WHITE);
			g.fillOval(x, y, width, width);
			x+=addX;
			y+=addY;
			g.setColor(c);
			g.fillOval(x, y, width, width);
		}

但这就面临着一个问题，程序在这个循环里的时候，就不能执行别的内容，也就是说只能控制一个球运动。如果想要让这个while循环可以多个同时执行，就需要用到线程。
b、怎样使用线程：
第一步：写一个类继承Thread类或者实现Runnable接口；
第二步：在第一步里的类里里重写run()方法，需要用线程执行的代码就放到run方法里面；
import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;

public class ThreaBall extends Thread {
	private Graphics g;
	private int x, y;

	public ThreaBall(Graphics g, int x, int y) {
		this.g = g;
		this.x = x;
		this.y = y;
	}

	/**
	 * 启动线程时执行的方法
	 */
	public void run() {
		Random rand = new Random();  //随机数对象，可以用于产生随机数
		int width = 20 + rand.nextInt(100);
		int red = rand.nextInt(255);  //产生0-255内的随机数
		int green = rand.nextInt(255);
		int blue = rand.nextInt(255);
		Color c = new Color(red, green, blue);  //利用红绿蓝三颜色生成一种颜色
		while (true) {
			try {
				Thread.sleep(30);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			double addX = -2+rand.nextInt(4000)*0.001;
			double addY = -2+rand.nextInt(4000)*0.001;
			g.setColor(Color.WHITE);
			g.fillOval(x, y, width, width);
			x+=addX;
			y+=addY;
			g.setColor(c);
			g.fillOval(x, y, width, width);
		}
	}
}

第三部：在监听器类里面相应地方实例化第一步里的类，在需要用到执行多线程的地方用第一步里的类的对象的start()方法执行run方法里的内容。
public void mouseClicked(MouseEvent e) {
		x = e.getX();
		y = e.getY();
		
		//创建线程对象
		ThreaBall tb = new ThreaBall(g,x,y);
		//启动线程
		tb.start();
	}

c、线程的优化：
用上面多线程写出的可以画出多个运动的小球在相互重叠的时候可以看到明显的闪动，因为不同小球画白色填充圆的时间不同，会有看起来冲突的时候。同时每个线程都相当于一个独立的程序，当有特别多的线程同时执行的时候，会影响程序效率。
其实说白了，计算机真正意义上可以同时执行的线程的数量取决于cpu的核数，一个cpu在某个时间点上只能执行1个线程。那计算机可以同时执行那么多线程的原因是什么？这是因为cpu执行一个线程的时间都特别快，当速度足够快的时候，这些线程看起来就像是同时执行的一样。
所以可以按照这个思路来优化上面的程序：将某一个时间内要画的所有的小球放到一个线程里完成，具体步骤如下：
第一步、写一个记录所有小球信息的类，将小球的左上角坐标和宽度、颜色、横纵坐标的改变值等给保存起来，同时在这个类里写一个将绘制小球的方法：
import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;

public class StoreBall {

	private int x, y,addX,addY;
	private Graphics g;
	private int w;
	private int red = 0, green = 0, blue = 0;
	private Color c;
	private StoreBall[] Ball;
    private Random ran = new Random();

	public StoreBall(int x, int y, Graphics g,StoreBall[] Ball) {
		addX=-5 + ran.nextInt(10);
		addY=-5 + ran.nextInt(10);
		w = 20 + ran.nextInt(50);
		red = ran.nextInt(255);
		green = ran.nextInt(255);
		blue = ran.nextInt(255);
	    c = new Color(red, green, blue);
		this.x = x;
		this.y = y;
		this.g = g;
		this.Ball = Ball;
	}
	
	public void ShowBall() {
		x += addX;
		y += addY;
		g.setColor(c);
		g.fillOval(x-w/2, y-w/2, w, w);
	}
}

第二步、在监听器里创建一个保存小球信息的对象的数组，在监听器的构造方法里实例化监听器对象并执行start()函数（确保只实例化一个监听器）：
private StoreBall[] Ball = new StoreBall[200];
public ClickListener(Graphics g) {
		this.g = g;
		ThreadRun tr = new ThreadRun(g, Ball);
		TimeThread tt = new TimeThread(Ball);
		tr.start();
		tt.start();
	}

第三步、鼠标点击的时候，创建一个保存小球信息的类的对象，将其保存进第二部创建的对象数组：
	public void mouseClicked(MouseEvent arg0) {
		int x = arg0.getX();
		int y = arg0.getY();
		StoreBall ball = new StoreBall(x, y, g,Ball);
		// ball.ShowBall();
		Ball[count++] = ball;
	}

第四步、在线程类的run方法里写一个for循环，循环开始前用背景颜色在真个窗口上画一个填充矩形，for循环历遍保存小球信息的对象，执行不为空的对象的画小球的方法：
public void ShowFrame() {
		g.setColor(Color.WHITE);
		g.fillRect(0, 0, 910, 910);  //用背景颜色填充整个窗口
		for (int i = 0; i < Ball.length; i++) {
			if (Ball[i] != null && Ball != null) {
				meetBall(Ball[i]);
				Ball[i].ShowBall();     //执行非空对象成员的画小球的方法
			}
		}
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

（2）、鼠标监听器的使用
第一步：写一个对象实现鼠标监听器接口KeyListener：
public class ClickListener implements KeyListener {}

第二步、重写所有KeyListener的成员函数，在keyPressed(KeyEvent e)方法里获取用户按下的按键，并根据按键执行相应的操作：
switch (e.getKeyCode()) {
		case KeyEvent.VK_UP:
			y-=10;
			break;
		case KeyEvent.VK_DOWN:
			y+=10;
			break;
		case KeyEvent.VK_RIGHT:
			x+=10;
			break;
		case KeyEvent.VK_LEFT:
			x-=10;
			break;
		}

第三步、给窗口添加这个类实例化的键盘监听器：
ClickListener l = new ClickListener(g);
jsf.addMouseListener(l);  	
jsf.addKeyListener(l);

2、	开发球球大作战
（1）、游戏规则
一个窗体里有许多不同大小的小球，其中一个玩家控制，玩家可以按住键盘上的方向键控制小球的运动方向。当小球碰到比自己小的小球，可以将其吃掉，小球变得更大，碰到比自己大的小球，玩家死亡。
（2）、代码需要实现的几个点：
a、非玩家小球的产生方法：我的产生方法：鼠标点击就会在20个随机位置产生大小不同、颜色不同的小球。同时还需要注意，如果有两个小球产生的位置重合了，就需要重新产生一个小球：
public void mouseClicked(MouseEvent arg0) {
		int tCount = 0;
		for (int i = 0; i < Ball.length; i++) {
			if (Ball != null && Ball[i] == null) {   //判断是为了防止空指针
				if (tCount++ >= 20)
					break;
				Ball[i] = addBall();
			}
		}
	}

	public StoreBall addBall() { // 随机产生小球
		Random rand = new Random();
		int x = rand.nextInt(900);
		int y = rand.nextInt(900);
		StoreBall ball = new StoreBall(x, y, g, Ball);
		for (int i = 0; i < Ball.length; i++) { // 防止产生重合的小球
			if (Ball[i] != null) {
				int cx = ball.getX() - Ball[i].getX();
				System.out.println("i:" + i);
				int cy = ball.getY() - Ball[i].getY();
				if (Math.sqrt(cx * cx + cy * cy) <= (ball.getW() + Ball[i].getW()) / 2) {
					ball = addBall();
				}
			}
		}
		return ball;
	}

b、非玩家小球的移动方法：可以随机向某个方向运动，持续一段时间后改变运动方向；
这里可以借助另一个监听器来实现，写一个监听器，在一段时间内改变控制小球运动方向的参数的值：
public class TimeThread extends Thread {
	private int addX, addY;
	Random rand = new Random();

	public void run() {
		while (true) {
			
			addX = -20 + rand.nextInt(40);
			addY = -20 + rand.nextInt(40);
			try {

				Thread.sleep(900);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	public int getAddX() {
		return addX;
	}

	public int getAddY() {
		return addY;
	}
}

c、非玩家小球的碰撞方法：两个小球碰撞时，两个小球的运动方向及速度相互变化，即第一个小球的运动方向和速度变成了第二个小球的运动方向和速度。小球与边框碰撞时要能够反弹（碰撞过程中如果只有涉及到一个小球，则只需要在储存小球的类里写碰撞方法，如果涉及多个小球，必须在声明了储存小球对象的数组里写碰撞方法）：
public void meetBall(StoreBall ball) {   //两个小球碰撞时，两个小球的运动方向及速度相互变化，即第一个小球的运动方向和速度变成了第二个小球的运动方向和速度。
		for (int i = 0; i < Ball.length; i++) {
			if (Ball[i] != null && ball != null && Ball[i] != ball) {
				int x = Ball[i].getX() - ball.getX();
				int y = Ball[i].getY() - ball.getY();
				if (Math.sqrt(x * x + y * y) <= (Ball[i].getW() + ball.getW()) / 2) {
					int addX1 = Ball[i].getAddX();
					int addY1 = Ball[i].getAddY();
					int addX2 = ball.getAddX();
					int addY2 = ball.getAddY();
					Ball[i].setAddX(addX2);
					Ball[i].setAddY(addY2);
					ball.setAddX(addX1);
					ball.setAddY(addY1);
				}
			}
		}
	}

public void meetWall(){   //小球与边框碰撞时要能够反弹
		if(x+w>910){
			addX=-10 + ran.nextInt(5);
		}
		if(x<40){
			addX=ran.nextInt(10);
		}
		if(y+w>910){
			addY=-10 + ran.nextInt(5);
		}
		if(y<60){
			addY=ran.nextInt(10);
		}
	}

d、玩家的小球与非玩家的小球碰撞，如果玩家小球的大小比非玩家小球的大小大的话，非玩家的小球消失，玩家的小球变大；否则玩家死亡，退出游戏：
public void meetGamer() {
		for (int i = 0; i < Ball.length; i++) {
			if (Ball[i] != null) {
				int x = Ball[i].getX() - gamer.getX();
				int y = Ball[i].getY() - gamer.getY();
				if (Math.sqrt(x * x + y * y) - (Ball[i].getW() + gamer.getW()) / 2 < 0) {
					if (Ball[i].getW() > gamer.getW()) {
						JOptionPane.showMessageDialog(null, "玩家死亡！");
						System.exit(0);
					} else {
						gamer.setW(gamer.getW() + Ball[i].getW() / 4);
						Ball[i] = null;
					}
				}
			}
		}
	}

3、	完整的代码示例：
–ShowFrame.java—
package com.antony.runBall0915;

import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;



public class ShowFrame {
    public static void main(String[] args) {
    	JFrame jsf = new JFrame();
    	jsf.setSize(910, 910);
    	jsf.setDefaultCloseOperation(3);
    	
    	jsf.setTitle("球球大作战");
        jsf.setLocationRelativeTo(null);
        jsf.getContentPane().setBackground(Color.WHITE);
        jsf.setVisible(true);
    	Graphics g = jsf.getGraphics();
        ClickListener l = new ClickListener(g);
        jsf.addMouseListener(l);  	
        jsf.addKeyListener(l);
    }
}

–ClickListener.java—
package com.antony.runBall0915;

import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Random;

public class ClickListener extends MouseAdapter implements KeyListener {
	private StoreBall[] Ball = new StoreBall[200];
	private int count = 0;
	private java.awt.Graphics g;
	private int x = 300, y = 500, w = 30;
	Gamer gamer;

	public ClickListener(Graphics g) {
		this.g = g;
		gamer = new Gamer(x, y, w, g);
		ThreadRun tr = new ThreadRun(g, Ball, gamer);
		TimeThread tt = new TimeThread(Ball);
		tr.start();
		tt.start();
	}

	public void mouseClicked(MouseEvent arg0) {
		int tCount = 0;
		for (int i = 0; i < Ball.length; i++) {
			if (Ball != null && Ball[i] == null) {
				if (tCount++ >= 20)
					break;
				Ball[i] = addBall();
			}
		}
	}

	public StoreBall addBall() { // 随机产生小球
		Random rand = new Random();
		int x = rand.nextInt(900);
		int y = rand.nextInt(900);
		StoreBall ball = new StoreBall(x, y, g, Ball);
		for (int i = 0; i < Ball.length; i++) { // 防止产生重合的小球
			if (Ball[i] != null) {
				int cx = ball.getX() - Ball[i].getX();
				System.out.println("i:" + i);
				int cy = ball.getY() - Ball[i].getY();
				if (Math.sqrt(cx * cx + cy * cy) <= (ball.getW() + Ball[i].getW()) / 2) {
					ball = addBall();
				}
			}
		}
		return ball;
	}

	public void keyTyped(KeyEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void keyPressed(KeyEvent e) {
		switch (e.getKeyCode()) {
		case KeyEvent.VK_UP:
			y -= 10;
			break;
		case KeyEvent.VK_DOWN:
			y += 10;
			break;
		case KeyEvent.VK_RIGHT:
			x += 10;
			break;
		case KeyEvent.VK_LEFT:
			x -= 10;
			break;
		}
		System.out.println("x:" + x + "y:" + y);
		if (gamer.getX() + gamer.getW() > 910) {
			x = 910 - gamer.getW();
		}
		if (gamer.getY() + gamer.getW() > 910) {
			y = 910 - gamer.getW();
		}
		if (gamer.getX() < 0) {
			x = gamer.getW();
		}
		if (gamer.getY() < 0) {
			y = gamer.getW();
		}
		gamer.setX(x);
		gamer.setY(y);
	}

	@Override
	public void keyReleased(KeyEvent e) {
		// TODO Auto-generated method stub

	}
}

–StoreBall.java–
package com.antony.runBall0915;

import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;

public class StoreBall {

	private int x, y,addX,addY;
	private Graphics g;
	private int w;
	private int red = 0, green = 0, blue = 0;
	private Color c;
	private StoreBall[] Ball;
    private Random ran = new Random();

	public StoreBall(int x, int y, Graphics g,StoreBall[] Ball) {
		addX=-5 + ran.nextInt(10);
		addY=-5 + ran.nextInt(10);
		w = 20 + ran.nextInt(50);
		red = ran.nextInt(255);
		green = ran.nextInt(255);
		blue = ran.nextInt(255);
	    c = new Color(red, green, blue);
		this.x = x;
		this.y = y;
		this.g = g;
		this.Ball = Ball;
	}
	
	public void setW(int w){
		this.w = w;
	}
	
	public void meetWall(){
		if(x+w>910){
			addX=-10 + ran.nextInt(5);
		}
		if(x<40){
			addX=ran.nextInt(10);
		}
		if(y+w>910){
			addY=-10 + ran.nextInt(5);
		}
		if(y<60){
			addY=ran.nextInt(10);
		}
	}
	
	
	public void setAddX(int x){
		addX = x;
	}
	
	public void setAddY(int y){
		addY = y;
	}
	
	public int getAddX(){
		return addX;
	}
	
	public int getAddY(){
		return addY;
	}
	
    public int getX() {
    	return x-w/2;
    }
    
    public int getY() {
    	return y-w/2;
    }
    
    public int getW() {
    	return w;
    }
 
	public void ShowBall() {
		//g.setColor(Color.WHITE);
		//g.fillOval(x-w/2, y-w/2, w, w); 
		meetWall();
		x += addX;
		y += addY;
		
	//	System.out.println("x:"+x+",y"+y+",Color:"+c);
		
		g.setColor(c);
		g.fillOval(x-w/2, y-w/2, w, w);
	}
}

–Gamer.java—
package com.antony.runBall0915;

import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;

public class Gamer{
	private int x, y, w;
	private Graphics g;
	Random rand = new Random();
	int red = rand.nextInt(255);
	int green = rand.nextInt(255);
	int blue = rand.nextInt(255);
	Color c = new Color(red, green, blue);

	public Gamer(int x, int y, int w, Graphics g) {
		this.x = x;
		this.y = y;
		this.w = w;
		this.g = g;
	}
	
    public int getX() {
    	return x-w/2;
    }
    
    public int getY() {
    	return y-w/2;
    }
    
    public int getW() {
    	return w;
    }

	public void setX(int x) {
		this.x = x;
	}
	
	public void setW(int w) {
		this.w = w;
	}

	public void setY(int y) {
		this.y = y;
	}

	public void showGamer() {
		g.setColor(c);
		g.fillOval(x, y, w, w);
	}
}

–ThreadRun.java—
package com.antony.runBall0915;

import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;

import javax.swing.JOptionPane;

public class ThreadRun extends Thread {
	private Graphics g;
	private StoreBall[] Ball;
	private Random rand = new Random();
	private Gamer gamer;

	public ThreadRun(Graphics g, StoreBall[] Ball, Gamer gamer) {
		this.g = g;
		this.Ball = Ball;
		this.gamer = gamer;
	}

	public void run() {
		while (true) {
			ShowFrame();
		}
	}

	public void meetGamer() {
		for (int i = 0; i < Ball.length; i++) {
			if (Ball[i] != null) {
				int x = Ball[i].getX() - gamer.getX();
				int y = Ball[i].getY() - gamer.getY();
				if (Math.sqrt(x * x + y * y) - (Ball[i].getW() + gamer.getW()) / 2 < 0) {
					if (Ball[i].getW() > gamer.getW()) {
						JOptionPane.showMessageDialog(null, "玩家死亡！");
						System.exit(0);
					} else {
						gamer.setW(gamer.getW() + Ball[i].getW() / 4);
						Ball[i] = null;
					}
				}
			}
		}
	}

	public void ShowFrame() {
		int count = 0;
		g.setColor(Color.WHITE);
		g.fillRect(0, 0, 910, 910);
		gamer.showGamer();
		for (int i = 0; i < Ball.length; i++) {
			// System.out.println("Ball[i]:"+Ball[i]+",i:"+i);
			if (Ball[i] != null && Ball != null) {
				count++;
				meetGamer();
				meetBall(Ball[i]);
				System.out.println("i:" + i);
				if (Ball[i] != null)
					Ball[i].ShowBall();
			}
		}
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public void meetBall(StoreBall ball) {
		for (int i = 0; i < Ball.length; i++) {
			if (Ball[i] != null && ball != null && Ball[i] != ball) {
				int x = Ball[i].getX() - ball.getX();
				int y = Ball[i].getY() - ball.getY();
				if (Math.sqrt(x * x + y * y) <= (Ball[i].getW() + ball.getW()) / 2) {
					int addX1 = Ball[i].getAddX();
					int addY1 = Ball[i].getAddY();
					int addX2 = ball.getAddX();
					int addY2 = ball.getAddY();
					Ball[i].setAddX(addX2);
					Ball[i].setAddY(addY2);
					ball.setAddX(addX1);
					ball.setAddY(addY1);
				}
			}
		}
	}
}

–TimeThread.java—
package com.antony.runBall0915;

import java.util.Random;

public class TimeThread extends Thread {
	private StoreBall[] Ball;
	Random rand = new Random();
	public TimeThread(StoreBall[] Ball){
		this.Ball = Ball;
	}
	public void run() {
		while (true) {
			for(int i=0;i<Ball.length;i++){
				if(Ball[i]!=null){
					Ball[i].setAddX( -5 + rand.nextInt(10));
					Ball[i].setAddY(-5 + rand.nextInt(10));
					try {
						Thread.sleep(2000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}
	}
}

</td></tr><tr><td>https://blog.csdn.net/weixin_41475710/article/details/82561131</td><td>#java    五子棋    java实现五子棋AI算法详解</td><td>


java实现五子棋 AI算法详解
学习本文内容需要先准备的知识：窗体编写、窗体元素添加、窗体绘制线条、窗体绘制填充圆
1、	前期准备：
(1)、棋盘的绘制：在一个窗口里绘制一定的水平线和垂直线，使之交叉构成棋盘；
(2)、棋子的呈现方法：鼠标点击指定位置的时候，获取指定位置的坐标，找到与所点击位置距离最近的棋盘上的线条交叉点，在此绘制一个填充圆（注意，java画圆的方法需要提供的参数有4个：圆左上角坐标、圆的宽度和圆的高度，所以不能直接将棋盘上某个点的坐标作为传入的参数值）；
(3)、判断输赢的方法：在每次下棋的这个点位向八个方向寻找，找到连续五个棋子颜色一致则判断胜利。
2、	需要用到的新知识——HashMap
HashMap是一个用于存储Key-Value键值对的集合，基于哈希表的 Map 接口的实现。
(1)、语法：
HashMap<键的参数类型,值的参数类型> HashMap名 = new HashMap<键的参数类型,值的参数类型>();

注意：键和值的类型只能为出了基本参数类型外的自定义参数类型，所以不能使用int、char、double等关键字作为键和值的类型，但可以使用它们对应的类名，如整形可以用Integer来替换。
(2)、为HashMap添加内容：HashMap名.put(键,值)；
(3)、利用键从HashMap里面获取对应的值：HashMap名.get(键)；
注意：从HashMap对象里面获取到的值的类型和HashMap定义时值的类型一致，如果需要获取并保存这个值，需要定义一个和HashMap值类型同类型的对象。
例：
HashMap<String,Integer> hm = new HashMap<String,Integer>(); //创建一个HashMap对象，键为字符串类型，值为整形
    hm.put("1",20); //给该HashMap对象添加一个键为”1”，值为20的数据；
Integer value = hm.get(“1”);  //获取该HashMap对象里面键为”1”对应的值。

3、	AI算法解析
(1)、文字说明：
第一步：在棋盘上没有棋子的位置向八个方向（上、下、左、右、左斜向上、左斜向下、右斜向上、右斜向下）搜索相同颜色的棋子，对手（相对于机器人，也就是玩家）的颜色用1表示，自己的用2表示，搜索到没有棋子的位置就停止搜索；
第二步：将附近最多4个棋子的颜色对应的数字连接成一个字符串，将其作为HashMap的键，按照对手连续棋子的数量为不同的键设置不同的值，连续的棋子越多值越大，周围没有棋子或者该点已经有棋子，则值为0；
第三步：将所有空白棋位的八个方向的值相加，记录起来，机器在值最大的点下棋。
(2)、代码示例：
private int[][] chessValue = new int[15][15];    //用于储存各个棋位的值
private HashMap<String, Integer> hm = new HashMap<String, Integer>();  //创建一个HashMap对象保存键和值

//为不同的键添加不同的值
public void setValue() {    
		hm.put("1", 20);
		hm.put("11", 200);
		hm.put("111", 2000);
		hm.put("1111", 3000);
		hm.put("12", 10);
		hm.put("112", 100);
		hm.put("1112", 1000);
		hm.put("11112", 2000);
}

//在棋盘上空白位置向八个方向搜索连续棋子，下面以向右方向为例
public void AI(int plaColor) {    传入对方棋子的颜色（1为黑，2为白）
		setValue(); //自定义的函数，用于将之前棋盘上各点的值chessValue[i][j]清空
		for (int i = 0; i < chess.length; i++) {  //chess是自定义的一个整形数组，存有各个棋位上棋子的颜色，黑色为1，白色为2，没有棋子为0
			for (int j = 0; j < chess[i].length; j++) {    //两个for循环，历遍所有的棋盘位置
				if (chess[i][j] == 0) {       //如果当前位置没有棋子则继续判断
					String code = "";
					int color = 0;
					// 从该位置向右，判断棋子颜色是否一样
					for (int k = i + 1; k < chess.length; k++) {
						if (chess[k][j] == 0) {   //如果遇到了没有棋子的棋位，跳出本次循环
							break;
						} else {
							if (color == plaColor) { // 判断右边第一个棋子的颜色是否是对手的颜色
								color = chess[k][j]; // 保存颜色
								code += 1; // 同色，记为1，组合进字符串
							} else if (chess[k][j] == plaColor) { // 右边第二，三..同颜色棋子
								code += 1; // 同色，记为1，组合进字符串
							} else { // 右边不同颜色
								code += 2; // 不同色，记为2，组合进字符串
								break;   //遇到一个不同色的即可跳出循环
							}
						}
                      //参照上面的方法可以写出向左、向上、向下、左斜向上、右斜向上、左斜向下、右斜向下的方法（具体见下面全部代码里的robot.java）
					}
					// 根据code取出hm对应的权值
					Integer value = hm.get(code);
					if (value != null) {
						chessValue[i][j] += value; // 权值累加
					}
              }
       }
}

4、全部的代码：
–ChessBoard.java–
package com.antony.wuziqi0908;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;

import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class ChessBoard {

	public static void main(String[] args) {
		ReFrame ChessBoard = new ReFrame();
		ChessBoard.setTitle("五子棋");
		ChessBoard.setSize(910, 910);
		ChessBoard.setDefaultCloseOperation(3);
		FlowLayout flow = new FlowLayout();
		BorderLayout boder=new BorderLayout();
		ChessBoard.setLayout(boder);
		JPanel p=new JPanel();
		p.setLayout(flow);
		Dimension d1 = new Dimension(60, 300);
		JLabel jsl_pla = new JLabel();
		jsl_pla.setPreferredSize(d1);
		JButton jsb_Whi = new JButton("玩家先下");
		JButton jsb_Bla = new JButton("机器先下");
		JButton jsb_Rob = new JButton("双人对战");
		JButton jsb_Ret = new JButton("悔棋");
		JButton jsb_Exi = new JButton("退出");
		Dimension d2 = new Dimension(60, 910);
		p.setPreferredSize(d2);
		p.add(jsl_pla);
		p.add(jsb_Whi);
		p.add(jsb_Bla);
		p.add(jsb_Rob);
		p.add(jsb_Ret);
		p.add(jsb_Exi);
		ChessBoard.getContentPane().add("East",p);
		Color boardColor = new Color(225, 176, 63);
		ChessBoard.getContentPane().setBackground(boardColor);
		p.setBackground(boardColor);
		ChessBoard.setLocationRelativeTo(null);
	    ChessBoard.setResizable(false);
		ChessBoard.setVisible(true);
		Graphics g = ChessBoard.getGraphics();
		DrawListener dl = new DrawListener(ChessBoard.SC,ChessBoard.SCF, g, ChessBoard);
		jsb_Whi.addActionListener(dl);
		jsb_Bla.addActionListener(dl);
		jsb_Rob.addActionListener(dl);
		jsb_Ret.addActionListener(dl);
		jsb_Exi.addActionListener(dl);
		ChessBoard.addMouseListener(dl);
	}
}

–DrawListener.java–
package com.antony.wuziqi0908;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JFrame;

public class DrawListener extends MouseAdapter implements ActionListener {
	private boolean canPlay = false, isPlayer = false;
	private int x, y, xx, yy;
	private Graphics g;
	private JFrame j;
	private int tWidth;
	public StoreChess[][] SC;
	public StoreChess[] SCF;
	private Robot r;
	// private int count = 0;
	private boolean isChess = true; // 判断点击的地方有没有已经下了的棋子
	private boolean isBlack = true; // 判断下一棋子是不是黑棋
	private String BT = null;
	private String oBT = null;
	private int count = 0;

	DrawListener(StoreChess[][] s, StoreChess[] sf, Graphics g, JFrame j) {
		this.g = g;
		this.j = j;
		SC = s;
		SCF = sf;
		r = new Robot(SC);
	}

	public int getTWidth() {
		int width = j.getWidth();
		int length = j.getHeight();
		if (length > width)
			tWidth = width;
		else
			tWidth = length;
		return tWidth;
	}

	public void mouseClicked(MouseEvent e) {
		r.clearValue();
		x = e.getX();
		y = e.getY();
		switch (BT) {
		case "玩家先下":
			oBT = BT;
			if (isPlayer) {
				playChess(x, y);
				isPlayer = false;
			}
			if (!isPlayer) {
				if (isBlack)
					r.AI(2);
				else
					r.AI(1);
				robPlayChess(r.getX(), r.getY());
				isPlayer = true;
			}
			break;

		case "机器先下":
			oBT = BT;
			if (isPlayer) {
				playChess(x, y);
				isPlayer = false;
			}
			if (!isPlayer) {
				if (isBlack)
					r.AI(2);
				else
					r.AI(1);
				robPlayChess(r.getX(), r.getY());
				isPlayer = true;
			}
			break;
		case "双人对战":
			oBT = BT;
			playChess(x, y);
			break;
		}
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		BT = e.getActionCommand();
		if (BT.equals("玩家先下")) {
			for (int i = 0; i < count - 1; i++) {
				SCF[i] = null;
			}
			count = 0;
			for (int i = 0; i < SC.length; i++) {
				for (int j = 0; j < SC[i].length; j++) {
					if (SC[i][j] != null) {
						SC[i][j] = null;
					}
				}
			}
			j.repaint();
			canPlay = true;
			isChess = false;
			if (count == 0)
				isPlayer = true;
		} else if (BT.equals("机器先下")) {
			for (int i = 0; i < count - 1; i++) {
				SCF[i] = null;
			}
			count = 0;
			for (int i = 0; i < SC.length; i++) {
				for (int j = 0; j < SC[i].length; j++) {
					if (SC[i][j] != null) {
						SC[i][j] = null;
					}
				}
			}
			j.repaint();
			canPlay = true;
			isChess = false;
			if (count == 0) {
				int[] rp = r.firstPlay();
				playChess(rp[0], rp[1]);
				isPlayer = true;
			}

		} else if (BT.equals("双人对战")) {
			for (int i = 0; i < count - 1; i++) {
				SCF[i] = null;
			}
			count = 0;
			for (int i = 0; i < SC.length; i++) {
				for (int j = 0; j < SC[i].length; j++) {
					if (SC[i][j] != null) {
						SC[i][j] = null;
					}
				}
			}
			j.repaint();
			canPlay = true;
			isChess = false;
		} else if (BT.equals("悔棋") && canPlay) {
			for (int i = 0; i < SC.length; i++) {
				for (int k = 0; k < SC[i].length; k++) {
					if (SC[i][k] != null) {
						if (SC[i][k].equals(SCF[count - 1])) {
							SC[i][k] = null;
						}
					}
				}
			}
			if (count > 0) {
				if (SCF[count - 1].getColor().equals(Color.BLACK)) {
					isBlack = true;
				} else {
					isBlack = false;
				}
				SCF[count - 1] = null;
				count--;
			}
			if(isPlayer==true){
				for (int i = 0; i < SC.length; i++) {
					for (int k = 0; k < SC[i].length; k++) {
						if (SC[i][k] != null) {
							if (SC[i][k].equals(SCF[count - 1])) {
								SC[i][k] = null;
							}
						}
					}
				}
				if (count > 0) {
					if (SCF[count - 1].getColor().equals(Color.BLACK)) {
						isBlack = true;
					} else {
						isBlack = false;
					}
					SCF[count - 1] = null;
					count--;
				}
			}
			BT = oBT;
			canPlay = true;
			isPlayer = true;
			// if (count <= 1) {
			// count = 0;
			// } else {
			// count = count - 2;
			// }
			j.repaint();
		}
	}

	public void playChess(int x, int y) {
		int d = (getTWidth() - 150) / 28;
		for (int i = 0; i < 15; i++) {
			int y1 = 100 + 2 * d * i;
			if (Math.abs(y - y1) < (getTWidth() - 150) / 28) {
				y = y1;
				yy = (y - 100) / (2 * d);
				break;
			}
		}
		for (int i = 0; i < 15; i++) {
			int x1 = 75 + 2 * d * i;
			if (Math.abs(x - x1) < (getTWidth() - 150) / 28) {
				x = x1;
				xx = (x - 75) / (2 * d);
				break;
			}
		}
		// g.setColor(BLACK);

		for (int i = 0; i < SC.length; i++) {
			for (int j = 0; j < SC[i].length; j++) {
				if (SC[i][j] != null) {
					if (x - d == SC[i][j].getX() && y - d == SC[i][j].getY()) {
						isChess = true;
						break;
					}
				}
			}
		}
		if ((!isChess) && canPlay) {
			if (isBlack) {
				g.setColor(Color.BLACK);
				g.fillOval(x - d, y - d, 2 * d, 2 * d);
				StoreChess chess = new StoreChess(x - d, y - d, 2 * d, g.getColor());

				// chess.draw(g);
				SC[xx][yy] = chess;
				SCF[count++] = chess;
				checkWinner();
				isBlack = false;
			} else {
				g.setColor(Color.WHITE);
				g.fillOval(x - d, y - d, 2 * d, 2 * d);
				StoreChess chess = new StoreChess(x - d, y - d, 2 * d, g.getColor());

				// chess.draw(g);
				SC[xx][yy] = chess;
				SCF[count++] = chess;
				checkWinner();
				isBlack = true;
			}
		}
		isChess = false;
	}

	public void robPlayChess(int x, int y) {
		int d = (j.getWidth() - 150) / 14;
		if ((!isChess) && canPlay) {
			if (isBlack) {
				g.setColor(Color.BLACK);
				g.fillOval(75 + x * d - d / 2, 100 + y * d - d / 2, d, d);
				StoreChess chess = new StoreChess(75 + x * d - d / 2, 100 + y * d - d / 2, d, g.getColor());

				SC[x][y] = chess;
				SCF[count++] = chess;
				checkWinner();
				isBlack = false;
			} else {
				g.setColor(Color.WHITE);
				g.fillOval(75 + x * d - d / 2, 100 + y * d - d / 2, d, d);
				StoreChess chess = new StoreChess(75 + x * d - d / 2, 100 + y * d - d / 2, d, g.getColor());

				SC[x][y] = chess;
				SCF[count++] = chess;
				checkWinner();
				isBlack = true;
			}
		}
		isChess = false;
	}

	public void checkWinner() {
		int d = (getTWidth() - 150) / 28;
		for (int i = 0; i < SC.length; i++) {
			for (int j = 0; j < SC[i].length - 4; j++) {
				if (SC[i][j] != null && SC[i][j + 1] != null && SC[i][j + 2] != null && SC[i][j + 3] != null
						&& SC[i][j + 4] != null) {
					if (SC[i][j].getColor().equals(SC[i][j + 1].getColor())
							&& SC[i][j].getColor().equals(SC[i][j + 2].getColor())
							&& SC[i][j].getColor().equals(SC[i][j + 3].getColor())
							&& SC[i][j].getColor().equals(SC[i][j + 4].getColor())) {
						SC[i][j].draw(g, Color.RED);
						SC[i][j + 1].draw(g, Color.RED);
						SC[i][j + 2].draw(g, Color.RED);
						SC[i][j + 3].draw(g, Color.RED);
						SC[i][j + 4].draw(g, Color.RED);
						isChess = true;
						canPlay = false;
					}
				}
			}
		}
		for (int i = 0; i < SC.length - 4; i++) {
			for (int j = 0; j < SC[i].length; j++) {
				if (SC[i][j] != null && SC[i + 1][j] != null && SC[i + 2][j] != null && SC[i + 3][j] != null
						&& SC[i + 4][j] != null) {
					if (SC[i][j].getColor().equals(SC[i + 1][j].getColor())
							&& SC[i][j].getColor().equals(SC[i + 2][j].getColor())
							&& SC[i][j].getColor().equals(SC[i + 3][j].getColor())
							&& SC[i][j].getColor().equals(SC[i + 4][j].getColor())) {
						SC[i][j].draw(g, Color.RED);
						SC[i + 1][j].draw(g, Color.RED);
						SC[i + 2][j].draw(g, Color.RED);
						SC[i + 3][j].draw(g, Color.RED);
						SC[i + 4][j].draw(g, Color.RED);
						isChess = true;
						canPlay = false;
					}
				}
			}
		}
		for (int i = 0; i < SC.length - 4; i++) {
			for (int j = 0; j < SC[i].length - 4; j++) {
				if (SC[i][j] != null && SC[i + 1][j + 1] != null && SC[i + 2][j + 2] != null && SC[i + 3][j + 3] != null
						&& SC[i + 4][j + 4] != null) {
					if (SC[i][j].getColor().equals(SC[i + 1][j + 1].getColor())
							&& SC[i][j].getColor().equals(SC[i + 2][j + 2].getColor())
							&& SC[i][j].getColor().equals(SC[i + 3][j + 3].getColor())
							&& SC[i][j].getColor().equals(SC[i + 4][j + 4].getColor())) {
						SC[i][j].draw(g, Color.RED);
						SC[i + 1][j + 1].draw(g, Color.RED);
						SC[i + 2][j + 2].draw(g, Color.RED);
						SC[i + 3][j + 3].draw(g, Color.RED);
						SC[i + 4][j + 4].draw(g, Color.RED);
						isChess = true;
						canPlay = false;
					}
				}
			}
		}
		for (int i = 4; i < SC.length - 4; i++) {
			for (int j = 0; j < SC[i].length; j++) {
				if (SC[i][j] != null && SC[i + 1][j - 1] != null && SC[i + 2][j - 2] != null && SC[i + 3][j - 3] != null
						&& SC[i + 4][j - 4] != null) {
					if (SC[i][j].getColor().equals(SC[i + 1][j - 1].getColor())
							&& SC[i][j].getColor().equals(SC[i + 2][j - 2].getColor())
							&& SC[i][j].getColor().equals(SC[i + 3][j - 3].getColor())
							&& SC[i][j].getColor().equals(SC[i + 4][j - 4].getColor())) {
						SC[i][j].draw(g, Color.RED);
						SC[i + 1][j - 1].draw(g, Color.RED);
						SC[i + 2][j - 2].draw(g, Color.RED);
						SC[i + 3][j - 3].draw(g, Color.RED);
						SC[i + 4][j - 4].draw(g, Color.RED);
						isChess = true;
						canPlay = false;
					}
				}
			}
		}
	}

}


–robot.java–
package com.antony.wuziqi0908;

import java.awt.Color;
import java.awt.Graphics;
import java.util.HashMap;
import java.util.Random;

public class Robot {
	private StoreChess[][] Chess;
	private int[][] chess = new int[15][15];
	private int[][] chessValue = new int[15][15];
	private int x,y;
	
	public int getX(){
		return x;
	}

	public int getY(){
		return y;
	}
	
	Robot(StoreChess[][] chess) {
		Chess = chess;
	}
	
	public void setChess(){
		for(int i=0;i<Chess.length;i++){
			for(int j = 0;j<Chess[i].length;j++){
				chess[i][j]=0;
				if(Chess[i][j]!=null){
					if(Chess[i][j].getColor()==Color.BLACK){
						chess[i][j]=1;
					}
					else if(Chess[i][j].getColor()==Color.WHITE){
						chess[i][j]=2;
					}
				}
			}
		}
	}

	private HashMap<String, Integer> hm = new HashMap<String, Integer>();

	public void setValue() {
		hm.put("1", 20);
		hm.put("11", 200);
		hm.put("111", 2000);
		hm.put("1111", 3000);
		hm.put("12", 10);
		hm.put("112", 100);
		hm.put("1112", 1000);
		hm.put("11112", 2000);
	}

	public int[] firstPlay(){
		int[] point = new int[2];
		Random rand = new Random();
		int x = 405+rand.nextInt(100);
		int y = 405+rand.nextInt(100);
		point[0]=x;
		point[1]=y;
		return point;
	}
	
	public void clearValue(){
		for(int i=0;i<chessValue.length;i++){
			for(int j=0;j<chessValue[i].length;j++){
				chessValue[i][j]=0;
			}
		}
	}
	
	public void AI(int plaColor) {
		setChess();
		setValue();
		for (int i = 0; i < chess.length; i++) {
			for (int j = 0; j < chess[i].length; j++) {
				if (chess[i][j] == 0) {
					String code = "";
					int color = 0;
					// 向右
					for (int k = i + 1; k < chess.length; k++) {
						if (chess[k][j] == 0) {
							break;
						} else {
							if (color == plaColor) { // 右边第一颗棋子
								color = chess[k][j]; // 保存颜色
								code += 1; // 保存棋局
							} else if (chess[k][j] == plaColor) { // 右边第二，三..同颜色棋子
								code += 1; // 保存棋局
							} else { // 右边不同颜色
								code += 2; // 保存棋局
								break;
							}
						}
					}
					// 根据code取出hm对应的权值
					Integer value = hm.get(code);
					if (value != null) {
						chessValue[i][j] += value;// 权值累加
					}

					// 向左
					for (int k = i - 1; k >= 0; k--) {
						if (chess[k][j] == 0) {
							break;
						} else {
							if (color == plaColor) { 
								color = chess[k][j]; // 保存颜色
								code += 1; // 保存棋局
							} else if (chess[k][j] == plaColor) {
								code += 1; // 保存棋局
							} else { 
								code += 2; // 保存棋局
								break;
							}
						}
					}
					// 根据code取出hm对应的权值
					value = hm.get(code);
					if (value != null) {
						chessValue[i][j] += value; // 权值累加
					}
					
					// 向上
					for (int k = j + 1; k < chess[i].length; k++) {
						if (chess[i][k] == 0) {
							break;
						} else {
							if (color == plaColor) { 
								color = chess[i][k]; // 保存颜色
								code += 1; // 保存棋局
							} else if (chess[i][k] == plaColor) { 
								code += 1; // 保存棋局
							} else {
								code += 2; // 保存棋局
								break;
							}
						}
					}
					// 根据code取出hm对应的权值
					value = hm.get(code);
					if (value != null) {
						chessValue[i][j] += value; // 权值累加
					}
					
					// 向下
					for (int k = j - 1; k > 0; k--) {
						if (chess[i][k] == 0) {
							break;
						} else {
							if (color == plaColor) {
								color = chess[i][k]; // 保存颜色
								code += 1; // 保存棋局
							} else if (chess[i][k] == plaColor) { 
								code += 1; // 保存棋局
							} else { 
								code += 2; // 保存棋局
								break;
							}
						}
					}
					// 根据code取出hm对应的权值
					value = hm.get(code);
					if (value != null) {
						chessValue[i][j] += value; // 权值累加
					}
					// 左斜向上
					for (int k = 1; i-k >0&&j-k>0 ; k++) {
						if (chess[i-k][j-k] == 0) {
							break;
						} else {
							if (color == plaColor) { 
								color = chess[i-k][j-k]; // 保存颜色
								code += 1; // 保存棋局
							} else if (chess[i-k][j-k] == plaColor) { 
								code += 1; // 保存棋局
							} else {
								code += 2; // 保存棋局
								break;
							}
						}
					}
					// 根据code取出hm对应的权值
					value = hm.get(code);
					if (value != null) {
						chessValue[i][j] += value; // 权值累加
					}
					// 右斜向下
					for (int k = 1; i+k<chess.length&&j+k<chess[i].length ; k++) {
						if (chess[i+k][j+k] == 0) {
							break;
						} else {
							if (color == plaColor) {
								color = chess[i+k][j+k]; // 保存颜色
								code += 1; // 保存棋局
							} else if (chess[i+k][j+k] == plaColor) { 
								code += 1; // 保存棋局
							} else { 
								code += 2; // 保存棋局
								break;
							}
						}
					}
					// 根据code取出hm对应的权值
					value = hm.get(code);
					if (value != null) {
						chessValue[i][j] += value; // 权值累加
					}
					// 右斜向上
					for (int k = 1; j-k>0&&i+k<chess[i].length ; k++) {
						if (chess[i+k][j-k] == 0) {
							break;
						} else {
							if (color == plaColor) {
								color = chess[i+k][j-k]; // 保存颜色
								code += 1; // 保存棋局
							} else if (chess[i+k][j-k] == plaColor) {
								code += 1; // 保存棋局
							} else { 
								code += 2; // 保存棋局
								break;
							}
						}
					}
					// 根据code取出hm对应的权值
					value = hm.get(code);
					if (value != null) {
						chessValue[i][j] += value; // 权值累加
					}
					// 左斜向下
					for (int k = 1; j+k<chess.length&&i-k>0; k++) {
						if (chess[i-k][j+k] == 0) {
							break;
						} else {
							if (color == plaColor) {
								color = chess[i-k][j+k]; // 保存颜色
								code += 1; // 保存棋局
							} else if (chess[i-k][j+k] == plaColor) {
								code += 1; // 保存棋局
							} else {
								code += 2; // 保存棋局
								break;
							}
						}
					}
					// 根据code取出hm对应的权值
					value = hm.get(code);
					if (value != null) {
						chessValue[i][j] += value; // 权值累加
					}
				}
				else{
					chessValue[i][j] = 0;
				}
			}
			
		}
		int max=0;
		for(int i = 0; i<chessValue.length-1;i++){
			for(int j=0; j<chessValue[i].length-1; j++){
				if(max<chessValue[i][j]){
					max = chessValue[i][j];
					x=i;
					y=j;
				}
			}
		}
	}
}


–ReFrame.java–
package com.antony.wuziqi0908;

import java.awt.Graphics;

import javax.swing.JFrame;

public class ReFrame extends JFrame {
	public StoreChess[][] SC = new StoreChess[15][15];
	public StoreChess[] SCF = new StoreChess[255];
	public void paint(Graphics g) {
		super.paint(g);
		drawBoard(g);
		drawChess(g);
		System.out.println("刷新棋盘！");
	}

	public void drawBoard(Graphics g) {
		int length = this.getHeight();
		int width = this.getWidth();
		int tWidth = 0;
		if (length > width)
			tWidth = width;
		else
			tWidth = length;
		for (int i = 0; i < 15; i++) {
			int x1 = 75 + (tWidth - 150) / 14 * 0;
			int y1 = 100 + (tWidth - 150) / 14 * i;
			int x2 = 75 + (tWidth - 150) / 14 * 14;
			int y2 = y1;
			g.drawLine(x1, y1, x2, y2);
			// System.out.println("x1:"+x1+",y1:"+y1+",x2:"+x2+",y2:"+y2+",(tWidth
			// - 150) / 14="+((tWidth - 150) / 14));
		}
		for (int i = 0; i < 15; i++) {
			int x1 = 75 + (tWidth - 150) / 14 * i;
			int x2 = x1;
			int y1 = 100 + (tWidth - 150) / 14 * 0;
			int y2 = 100 + (tWidth - 150) / 14 * 14;
			g.drawLine(x1, y1, x2, y2);
		}
	}
	// public int getTWidth(){
	// int length = this.getHeight();
	// int width = this.getWidth();
	// int tWidth = 0;
	// if (length > width)
	// tWidth = width;
	// else
	// tWidth = length;
	// return tWidth;
	// }

	public void drawChess(Graphics g) {
		for (int i = 0; i < SC.length; i++) {
			for (int j = 0; j < SC[i].length; j++) {
				if (SC[i][j]!= null) {
					SC[i][j].draw(g,SC[i][j].getColor());
				}
			}
		}
	}
}


–StoreChess.java–
package com.antony.wuziqi0908;

import java.awt.Color;
import java.awt.Graphics;

public class StoreChess {
	private int x, y,colorNum;
	private int width;
	private Color c;
	
	
    public int getX(){
    	return x;
    }
    
    public int getY(){
    	return y;
    }
    
    
    public Color getColor(){
    	return c;
    }
    
	StoreChess(int x, int y, int w, Color c) {
		this.x = x;
		this.y = y;
//		System.out.println("x="+ x);
//		System.out.println("y="+ y);
		this.c = c;
		width = w;
	}

	void draw(Graphics g,Color nC) {
		g.setColor(nC);
//		for (int i = 0; i < 15; i++) {
//			double y1 = 100 + (nTWidth - 150) / 14 * i;
//			if (Math.abs(y - y1) < (nTWidth - 150) / 28) {
//				y = y1;
//				break; 
//			}
//		}
//		for (double i = 0; i < 15; i++) {
//			double x1 = 75 + (nTWidth - 150) / 14 * i;
//			if (Math.abs(x - x1) < (nTWidth - 150) / 28) {
//				x = x1;
//				break;
//			}
//		}

//		System.out.println("nTWidth="+nTWidth);
//		System.out.println("FTW="+FTW);
//		System.out.println("proportion = "+proportion);
//		System.out.println("x - d="+(x - d));
//		System.out.println("y - d="+(y - d));
		
//		System.out.println("draw:"+((xx*r)+75 - d) + "," + ((yy*r)+100 - d));
//		System.out.println("r:"+r+", xx:"+xx+", d:"+d);
		g.fillOval(x,y,width,width);
	}
}


</td></tr><tr><td>https://blog.csdn.net/weixin_41475710/article/details/82254867</td><td>#java    接口的使用以及接口存在的意义揭秘</td><td>


java接口的使用以及接口存在的意义揭秘
1、	什么是接口
Java接口是一系列方法的声明，是一些方法特征的集合，一个接口只有方法的特征没有方法的实现，因此这些方法可以在不同的地方被不同的类实现，而这些实现可以具有不同的行为（功能）。
Java接口，Java语言中存在的结构，有特定的语法和结构（以上摘自百度百科）。
2、	接口怎么用？
（1）、第一步，定义一个接口，格式如下：
    [访问说明符] interface 接口名称  {
        // 声明变量
        // 抽象方法
}

说明：a.接口里的方法必须是public abstract（默认public abstract，这两个修饰符可不写）；b.接口里的变量默认为public static final （必须是public）；c.接口里只能放方法的声明，而不能定义。
（2）、第二步，定义一个类来实现这个接口，格式如下：
访问说明符 class 类名 ...implements 接口名称[, 其他接口名称, 其他接口名称..., ...] ...{
    //接口的所有方法的实现
    //类的属性
    //类的方法
}

说明：a.一个类可以实现多个接口，这些接口放在implements关键字后面，用半角逗号隔开；b.实现接口的类必须实现接口里所有的方法
3、	接口的继承
接口可以继承，而且可以多继承，格式如下：
    [可见度] interface 接口名称 [extends 其他的接口名, 其他的接口名, 其他的接口名……] {
        // 声明变量
        // 抽象方法
}

4、	为什么要用接口？
接下来用一个例子来说明为什么要用接口：
我有两支笔，但我不知道这两支笔分别是什么笔，写出来的字是什么样子的，于是建立了一个笔的接口，如下：
    public interface pen {
    public void write();    
}

我打开了其中一支笔的笔套，发现是一支钢笔，于是写了一个钢笔类来实现这个笔的接口：
    public class fountainPen implements pen {
	public void write() {
		System.out.println("写了一个钢笔字");
	}
}


再后来，我打开了另一支笔的笔套，发现是一支铅笔，于是又写了一个铅笔的类来实现这个接口：
  public class pencil implements pen{
	public void write() {
		System.out.println("写了一个铅笔字");
	}
}

综上，接口的存在方便了我们对未知类的编写，我们可以把我们不清楚这个类具体要拿来做什么，要有些什么操作，就可以先写一个接口，在清楚了以后再来实现。
</td></tr><tr><td>https://blog.csdn.net/weixin_41475710/article/details/82841911</td><td>#java    反射实现类外访问类的私有成员</td><td>


java    用反射实现类外访问类的私有成员
学习本文需要提前掌握的知识：java类与对象
1、 思路分析
想要实现访问类的私有成员，我们有两个思路，一个是将私有成员转换成公有成员，另一个思路是在访问私有成员的时候绕过java语言检测。
第一个思路，将私有成员转换成公有成员：这就和我们平常获取类的私有属性所用的方法一致——为这个私有成员写一个公有的get方法。
public void getInformation(){

    return 私有变量名；

}

public void carryFuntion(){

    私有方法；

}

像上面这样即可访问类的私有属性和方法，但这种方法需要在原类的基础上修改。来看下第二种方法，绕过java语言检测，这需要用到新的java知识——反射。
2、 java反射——另一种对象实现的方法
（1）、Class类：Class 类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类，注释是一种接口。每个数组属于被映射为 Class 对象的一个类，所有具有相同元素类型和维数的数组都共享该 Class 对象。基本的 Java 类型（boolean、byte、char、short、int、long、float 和 double）和关键字 void 也表示为 Class 对象。
此类的部分相关方法：
forName(String className) //返回与带有给定字符串名的类或接口相关联的 Class 对象，className为所需类的完全限定名。

getConstructor() //获取当前对象的构造函数

getMethod(String name)  //获取此类的公有方法，name为方法名，后面可以放此方法的参数

getDeclaredMethod(String name)  //获取此类的所有方法，name为方法名，后面可以放此方法的参数

（2）、Constructor类：提供关于类的单个构造方法的信息以及对它的访问权限。
此类的部分相关方法：
newInstance()  //调用此对象的构造函数创建对象

（3）、Method类：提供关于类或接口上单独某个方法（以及如何访问该方法）的信息。
此类的部分相关方法：
 //将此对象的 accessible 标志设置为指示的布尔值。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查。
    mPrivate.setAccessible(Boolean flag); 
 //执行该方法，obj为从中调用底层方法的对象，args为用于方法调用的参数。
    invoke(Object obj, Object... args)

3、使用反射绕过java语言检测
首先创建一个对象
public class Try{

    private void privateTest(){

           System.out.println("这是role对象的私有方法");

    }

    public void publicTest(){

           System.out.println("这是role对象的公有方法");

    }

}

在主函数里写一个方法，先获取相关对象，然后获取其构造函数，执行构造函数创建对象，获取此对象的相关方法（如果该方法是非公有方法需要用getDeclaredMethod方法获取），非公有方法需要用setAccessible绕过java语言检测，最后执行这些方法即可。
public static void testMethod() throws Exception {

              // forName返回与带有给定字符串名的类或接口相关联的Class对象。

              Class s =
Class.forName("com.antony.inherit0726.Try");

              /*

               * 返回一个 Constructor 对象，它反映此 Class 对象所表示的类的指定公共构造方法。参数是 Class 对象的一个数组，这些

               * Class 对象按声明顺序标识构造方法的形参类型。

               */

              Constructor con = s.getConstructor();

              // 创建对象

              Object obj = con.newInstance(null);

              // 获取类的方法

              Method mPublic = s.getMethod("publicTest");

              // 可以获取所有方法

              Method mPrivate = s.getDeclaredMethod("privateTest");

              /*

               * 将此对象的 accessible 标志设置为指示的布尔值。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为false 则指示反射的对象应该实施 java语言访问检查。
               */

              mPrivate.setAccessible(true);

              // 调用方法

              mPublic.invoke(obj);

              mPrivate.invoke(obj);

       }

</td></tr><tr><td>https://blog.csdn.net/weixin_41475710/article/details/82926105</td><td>#java    聊天室（二）——  给聊天室增加菜单和私聊功能</td><td>
#java    聊天室（二）——  给聊天室增加菜单和私聊功能
 
学习本文需要先查看我的上一篇博客： #java 聊天室（一）—— 实现一个简单Telnet多人群聊聊天室
在上一篇博客里，我们实现了用java写了一个telnet聊天服务器，实现了群聊功能。今天我们就来给这个聊天室添加菜单，并且实现私聊功能。 
1.实现目标 
 在用户登录后显示菜单：

当用户输入1后用户进入公共聊天室，提示用户输入想给大家发的消息，输入x退出公共聊天室：

当用户输入2后，显示出在线用户的列表，输入想私聊用户对应的编号即可与用户私聊，x退出： 

 
2.思路解析
(1)、处理用户的错误输入：
用户可能输入很奇怪的内容，因此我们需要用一些条件语句严格判断用户输入，只允许用户输入指定内容。在用户输入正确后执行相关操作，输入错误时让用户重新输入。 实现重新输入可以将这一系列的代码放在一个while循环里面，如果需要退出这些代码，用break即可实现。
例如：

//主菜单的实现
while (start) {
		showMenu();
	    String choice = bs.readLine();
	    if (choice != null) {
			switch (choice) {
			case "1":
				setMsg(bs);
				break;
			case "2":
				personList();
				break;
			case "3":
				sk.close();
				start = false;
				break;
			default:
				out.write("输入错误，请重新输入！".getBytes());
			}
		}
	}
有些地方只允许用户输入数字，可以用下面的方法判断一个字符串是不是数字：

// 判断一个字符串是不是数字
	public static boolean isNumeric(String str) {
		for (int i = str.length(); --i >= 0;) {
			if (!Character.isDigit(str.charAt(i))) {
				return false;
			}
		}
		return true;
	}
 
(2)、私聊功能的实现
a、显示用户列表：
从储存用户信息的Message队列里面获取用户名，将用户的索引和用户名输出到用户控制台，用户输入聊天对象的索引开始私聊。这里的私聊需要能一直持续，而不是输入了一句还需要重新选择对象。另外也允许用户输入指定命令退出私聊，因此可以把私聊功能放在一个while循环里面，当用户输入指定命令时执行break退出循环：

	// 进入私人聊天模块，列出在线用户
	public void personList() throws IOException {
		while (true) {
			out.write("以下是在线的用户，请选择你要聊天的用户\r\n".getBytes());
			for (int i = 0; i < msg.size(); i++) {
				if (msg.get(i).getName() != null) {
					out.write((i + "." + msg.get(i).getName() + "\r\n").getBytes());
				}
			}
			out.write("x.返回\r\n".getBytes());
			String strChoice = bs.readLine();
            //如果用户输入了“x”，退出
			if (strChoice.equals("x"))
				break;
            //判断用户输入的是不是数组，用户输入的数字即为Message队列里对应用户的索引
			else if (isNumeric(strChoice)) {
				int index = Integer.parseInt(strChoice);
				if (index >= 0 && index < msg.size()) {
                    //给索引为index的用户发消息，此函数在下面介绍
					chatPerson(index);
				}
			} else {
				out.write("输入错误，请重新输入！\r\n".getBytes());
			}
		}
	}
b、私聊功能的实现：
在Message类（这个类在我的前一篇博客或者下面的"完整的代码"模块里有全部内容）里面有连个方法需要提一下，一个是将消息储存的方法，一个是将消息输出的方法：

//储存消息
public void setMsg(String msg) {
		this.msg = msg;
	}

//将消息输出
public void outPut() throws IOException {
		out.write(msg.getBytes());
	}
 我们只需要将用户输入的消息存入Message队列里要私聊用户的对象，并执行私聊用户对象的outPut()方法即可在该用户的控制台输出私聊消息。同时也需要注意，这里也需要给用用户一个退出私聊的命令。所以和上面一样，将整个代码块放在一个while循环里面，用户输入指定命令后执行break即可。

//获取用户的输入，在索引为index的用户的控制台显示，即实现私聊功能。
	public void chatPerson(int index) throws IOException {
		while (true) {
			out.write(("请输入给" + msg.get(index).getName() + "发送的消息，输入x返回：\r\n").getBytes());
			String strMsg = bs.readLine();
			if (strMsg.equals("x"))
				break;
			out.write(("你对" + msg.get(index).getName() + "说：" + strMsg + "\r\n").getBytes());
			msg.get(index).setMsg(msg.get(count).getName() + "对你说:" + strMsg + "\r\n");
			msg.get(index).outPut();
		}
	}

 
3.完整的代码
——Manage.java——


import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

public class Manage {
	static ArrayList<Message> m = new ArrayList<>();
	static int count = 0;
	static ServerSocket sever;

	public static void main(String[] args) {
		try {
			sever = new ServerSocket(6666);
			System.out.println("服务器已开启。");
			while (true) {
				Socket sk = sever.accept();
				Sever ss = new Sever(m, sk, count++);
				Thread ts = new Thread(ss);
				ts.start();
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

——Message.java——


import java.io.IOException;
import java.io.OutputStream;
import java.net.SocketException;
import java.util.ArrayList;

public class Message {
	private String name;
	private ArrayList<Message> arrMsg;
	private boolean start = true;
	private OutputStream out;
	private String msg;

	public Message(String name, OutputStream out, ArrayList<Message> arrMsg) {
		this.name = name;
		this.out = out;
		this.arrMsg = arrMsg;
	}

	public boolean getStart() {
		return start;
	}

	public String getName() {
		return name;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}

	public void setMsgAll(String msg) {
		this.msg = msg;
		for (int i = 0; i < arrMsg.size(); i++) {
			arrMsg.get(i).setMsg(msg);
		}
		outPutAll();
	}

	public void exit() {
		start = false;
	}

	public void outPutAll() {
		for (int i = 0; i < arrMsg.size(); i++) {
			try {
				arrMsg.get(i).outPut();
			} catch (SocketException e1) {
				arrMsg.get(i).exit();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	public void outPut() throws IOException {
		out.write(msg.getBytes());
	}
}

——Sever.java——


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.util.ArrayList;

public class Sever implements Runnable {
	private ArrayList<Message> msg;
	private Socket sk;
	private Message m;
	private int count;
	private OutputStream out;
	private BufferedReader bs;
	private InputStream in;
	private boolean start = true;

	public Sever(ArrayList<Message> msg, Socket sk, int count) {
		this.msg = msg;
		this.sk = sk;
		this.count = count;
	}

	public void run() {
		recieve();
	}

	// 判断一个字符串是不是数字
	public static boolean isNumeric(String str) {
		for (int i = str.length(); --i >= 0;) {
			if (!Character.isDigit(str.charAt(i))) {
				return false;
			}
		}
		return true;
	}

	// 用户登陆后显示菜单
	public void showMenu() throws IOException {
		out.write("1.公共聊天室\r\n".getBytes());
		out.write("2.给某用户发私信\r\n".getBytes());
		out.write("3.退出\r\n".getBytes());
	}

	// 实现群聊功能
	public void setMsg(BufferedReader bs) throws IOException {
		out.write("欢迎来到公共聊天室，请输入您想说的话，输入x退出：\r\n".getBytes());
		while (msg.get(count).getStart()) {
			out.write("请输入您想对大家说的话，输入x退出：\r\n".getBytes());
			String strMsg = bs.readLine();
			if (strMsg != null) {
				if (strMsg.equals("x"))
					break;
				else
					msg.get(count).setMsgAll(msg.get(count).getName() + " said:" + strMsg + "\r\n");
			}
		}
	}

	// 用户登录后初始化
	public void login() throws IOException {
		out = sk.getOutputStream();
		in = sk.getInputStream();
		out.write("welcome!Please enter your username:\r\n".getBytes());
		bs = new BufferedReader(new InputStreamReader(in));
		String name = bs.readLine();
		out.write(("Your username is:" + name + "\r\n").getBytes());
		m = new Message(name, out, msg);
		count = msg.size();
		msg.add(count, m);
		msg.get(count).setMsgAll(name + " is on line.\r\n");
	}

	// 进入私人聊天模块，列出在线用户
	public void personList() throws IOException {
		while (true) {
			out.write("以下是在线的用户，请选择你要聊天的用户\r\n".getBytes());
			for (int i = 0; i < msg.size(); i++) {
				if (msg.get(i).getName() != null) {
					out.write((i + "." + msg.get(i).getName() + "\r\n").getBytes());
				}
			}
			out.write("x.返回\r\n".getBytes());
			String strChoice = bs.readLine();
			if (strChoice.equals("x"))
				break;
			else if (isNumeric(strChoice)) {
				int index = Integer.parseInt(strChoice);
				if (index >= 0 && index < msg.size()) {
					chatPerson(index);
				}
			} else {
				out.write("输入错误，请重新输入！\r\n".getBytes());
			}
		}
	}

	// 私人聊天
	public void chatPerson(int index) throws IOException {
		while (true) {
			out.write(("请输入给" + msg.get(index).getName() + "发送的消息，输入x返回：\r\n").getBytes());
			String strMsg = bs.readLine();
			if (strMsg.equals("x"))
				break;
			out.write(("你对" + msg.get(index).getName() + "说：" + strMsg + "\r\n").getBytes());
			msg.get(index).setMsg(msg.get(count).getName() + "对你说:" + strMsg + "\r\n");
			msg.get(index).outPut();
		}
	}

	public void recieve() {
		try {
			login();
			while (start) {
				showMenu();
				String choice = bs.readLine();
				if (choice != null) {
					switch (choice) {
					case "1":
						setMsg(bs);
						break;
					case "2":
						personList();
						break;
					case "3":
						sk.close();
						start = false;
						break;
					default:
						out.write("输入错误，请重新输入！".getBytes());
					}
				}
			}
			out.close();
			in.close();
			sk.close();
			msg.remove(count);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
  </td></tr><tr><td>https://blog.csdn.net/weixin_41475710/article/details/81782962</td><td>#java    一个简单的例子理解java继承、成员函数重写、类型转换</td><td>


一个简单的例子理解java继承、成员函数重写、类型转换
1、继承简介
举一个简单的例子：笔分为很多种，比如毛笔、钢笔、圆珠笔等等，这些笔都有一些相同的属性比如长度、笔迹的粗细等等；但他们也有不同的特点，比如他们的用途（毛笔可以写对联、钢笔可以写作业等等）等。这时候要编写一个程序用类表示这些笔，一般的方法就必须把各种笔的众多相同的属性重复写很多次，就很浪费时间。这时候就可以借助继承，先写一个拥有所有笔共同点的类，再分别写每一种笔的类，让其继承这个共同类即可。
先写一个笔的类：
public class pen {
	//定义长度
    private int length;
    //定义品牌
    private String brand;
    //定义笔迹的粗细
    private int width;
    public void setLength(int l){
    	length = l;
    }
    public int getLength(){
      	return length;
    }
    public void setBrand(String b){
      	brand = b;
    }
    public String getBrand(){
      	return brand;
    }
    public void setWidth(int w){
      	width = w;
    }
    public int getWidth(){
      	return width;
}
public void write(){
      	System.out.println("写了一个字");
} 
}


这时候写毛笔的类的时候，对于笔的共同属性就只需要先用extends关键字继承，再写其特有的属性和方法即可。（继承的格式class 子类/派生类 extends 父类/基类/超类 ，基类/父类/超类 就是拥有共同属性和方法的类， 子类/派生类则是从父类继承过来，拥有它自己特有的属性和方法的类。）
public class inkBrush extends pen{
    public void writeCouplet(){
    	System.out.println("写了一副对联");
    }
}

同样，钢笔类也只需先继承再写钢笔独有的属性和方法。
public class pencil extends pen{
    public void doHomework(){
    	System.out.println("完成了一次作业。");
    }
}

2、关于类类里面可以被继承的部分
Java里面的子类可以继承父类的所有属性和方法，只不过不能直接访问私有成员，默认的成员不能跨包继承。
3、关于方法的重写
不同的笔都可以写字，但不同的笔写出的子是不一样的。这时候就需要在各种笔的子类里重写write方法，如下所示：
public class pencil extends pen{
    public void write(){
        System.out.println("用钢笔写了一个字");
    }
}

子类里重写的方法必须和父类里的方法的关键字、方法名、方法里的参数个数以及每一个参数的属性都必须相同。不然就成了方法的重载。
3、关于类的类型转换
每种笔都可以用来写字。当一个学生用钢笔写字的时候，我们可以说“他在用钢笔写字”，也可以说“他在用笔写字“。用类表示如下：
写一个学生类：
public class student {
	public void write(pen p){
		p.write();
	}
}

主函数里面：
public class Manage {
    public static void main(String [] args){
        student stu1 = new student();
        pencil p1 = new pencil();
        stu1.write(p1);    	
    }
}

学生类的doHomework方法里的参数是pen的类型,主函数里student对象stu1的doHomework方法里的参数是pencil，但并没有报错。这说明pencil自动转换为了pen对象。
自动类型转换只能从子类转换为父类，不能从父类转换为子类，从父类转换为子类必须使用强制类型抓换。
一个学生在写作业的时候，我们可以说“他用钢笔写作业”，也可以不用说明他用什么笔写作业，直接说“他用笔写作业”。用类表示如下：
写一个学生类：
public class student {
    public void doHomework(pen p){
     	p.doHomework();
    }
}

如果按照上述写法会报错，这是因为pen这个类没有doHomework的方法，doHomework时pen的派生类pencil里的方法。着也说明pen类的对象无法转换成pencil类对象。这时候可以采用强制类型转换（强制类型转换的格式：   转换前的类名 转换前的对象名 = （转换后的类名）需转换的对象名）。如下所示：
public class student {
    public void doHomework(pencil p){
     	p.doHomework();
    }
}

</td></tr><tr><td>https://blog.csdn.net/weixin_41475710/article/details/82912220</td><td>#java    聊天室（一）——  实现一个简单Telnet多人群聊聊天室</td><td>
#java    聊天室（一）——  实现一个简单Telnet多人群聊聊天室
 
学习本文需要先准备的知识点：队列（或数组）、类与对象、线程、InputStream和OutputStream的使用。
1.前期准备（新知识点讲解）
（1）、认识Telnet
a、关于Telnet
Telnet协议是TCP/IP协议族中的一员，是Internet远程登陆服务的标准协议和主要方式。它为用户提供了在本地计算机上完成远程主机工作的能力。在终端使用者的电脑上使用telnet程序，用它连接到服务器。终端使用者可以在telnet程序中输入命令，这些命令会在服务器上运行，就像直接在服务器的控制台上输入一样。可以在本地就能控制服务器。要开始一个telnet会话，必须输入用户名和密码来登录服务器。Telnet是常用的远程控制Web服务器的方法。
b、telnet的工作过程：
(a)、本地与远程主机建立连接。该过程实际上是建立一个TCP连接，用户必须知道远程主机的Ip地址或域名；
(b)、将本地终端上输入的用户名和口令及以后输入的任何命令或字符以NVT（Net Virtual Terminal）格式传送到远程主机。该过程实际上是从本地主机向远程主机发送一个IP数据包；
(c)、将远程主机输出的NVT格式的数据转化为本地所接受的格式送回本地终端，包括输入命令回显和命令执行结果；
(d)、最后，本地终端对远程主机进行撤消连接。该过程是撤销一个TCP连接。
（以上关于telnet的内容摘自百度百科）
    我们需要做的，就是写一个服务器，使用户能够连接上我们的服务器，而且我们的服务器能够对用户发送的消息进行处理。
(2)、BufferedReader类：用于从字符输入流中读取文本，缓冲各个字符，从而实现字符、数组和行的高效读取。此类的对象创建时，需要传入一个InputStream对象。
此类的部分相关方法：

read();  // 读取单个字符。

readLine(); //读取一个文本行，程序运行到这里时需要用户输入，将用户输入的数据读取。
例如：

bs = new BufferedReader(new InputStreamReader(in));
String name = bs.readLine();
(3)、ServerSocket类：此类实现服务器套接字。服务器套接字等待请求通过网络传入。它基于该请求执行某些操作，然后可能向请求者返回结果。简而言之，就是在指定端口创建一个服务器。此类的对象创建时，需要传入的数字就是端口。
       此类的相关方法：

accept();  //侦听并接受到此套接字的连接。此方法在连接传入之前一直阻塞。即接受用户连接，在用户未连接时程序会在此暂停。其返回值是一个Socket对象。
例如：

//在6666端口创建一个服务器，等待用户连接（虽然创建了，但不会对用户的连接作出反应）。
ServerSocket sever = new ServerSocket(6666);Socket sk = sever.accept();
(4)、Socket类：此类实现客户端套接字（也可以就叫“套接字”）。套接字是两台机器间通信的端点。 可以利用Socket对象与客户端进行交互。
       此类的相关方法：

getOutputStream();  //返回此套接字的输出流。即返回用户端的输出流，可以借助此输出流显示传递给用户的信息。

getInputStream();  //返回此套接字的输入流。即返回用户端的输入流，可以借助此输入流获取用户端的输入信息。
 
2.思路分析
(1)、建立一个服务器，实现相应的端口，在用户连接的时候启动一个线程，用于获取用户的输入和输出，为了能够让多个用户连接，可以将侦听用户连接的代码放在一个while(true)循环里面（侦听的语句在没有用户连接的时候会在此暂停，不用担心死循环造成的程序卡顿等问题）：

//建立一个Message对象的队列，用于储存用户的信息，Message对象将会在后面介绍
static ArrayList<Message> m = new ArrayList<>();
static ServerSocket sever;
	public static void main(String[] args) {
		try {
//创建一个服务器，实现6666端口
			sever = new ServerSocket(6666);
			System.out.println("服务器已开启。");
			while (true) {
                             //侦听用户连接，没有用户连接的时候会在此停留
				Socket sk = sever.accept();
                             //有用户连接，创建一个线程控制用户的输入输出
				Sever ss = new Sever(m,sk);
				Thread ts = new Thread(ss);
                             //启动线程
				ts.start();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

(2)、建立一个对象，储存用户输入的信息，并且执行改用户的输出操作

public class Message {
       //msg用于储存用户输入的消息
	private String msg;
       //name储存用户姓名
	private String name;
       //判断用户有没有输入
	private boolean isNewPut = true;
       //判断用户是不是第一次进入
	private boolean isNewJoin = true;
       //用于控制线程的开始停止
	private boolean start = true;

	public void setName(String name) {
		this.name = name;
	}
	
	public boolean getStart() {
		return start;
	}

	public String getName() {
		return name;
	}

	public Message() {
		msg = new String();
	}

       //获取用户输入的信息，将判断是否有用户输入的boolean值改为true，这个值为true时，下面介绍的的Together线程将会向所有用户输出此用户的消息
	public void addMsg(String msg) {
		this.msg = msg;
		isNewPut = true;
	}

	public String getMsg() {
		return msg;
	}

	public void setIsNewPut(boolean s) {
		isNewPut = s;
	}

	public boolean getIsNewPut() {
		return isNewPut;
	}

	public void setIsNewJoin(boolean j) {
		isNewJoin = j;
	}

	public boolean getIsNewJoin() {
		return isNewJoin;
	}

       //用户退出时，将strat、isNewPut改成false
	public void exit() {
		start = false;
		isNewPut = false;
	}
}

(3)、编写控制用户输入输出的线程

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;

public class Sever implements Runnable {
        //用于储存用户的Message队列
	private ArrayList<Message> msg;
       	private Socket sk;
	private Message m;
        //count可用作区分用户的索引
	private int count;
	private OutputStream out;
	private BufferedReader bs;
	private InputStream in;

	public Sever(ArrayList<Message> msg, Socket sk, int count) {
		this.msg = msg;
		this.sk = sk;
		this.count = count;
	}

	public void run() {
		recieve();
 	}

        //将用户的输出传给所有用户，并在所有用户的控制台输出
	public void setMsg(BufferedReader bs) throws IOException {
		while (msg.get(count).getStart()) {
			String strMsg = bs.readLine();
			if (strMsg != null)
				msg.get(count).setMsgAll(msg.get(count).getName() + " said:" + strMsg + "\r\n");
		}
	}

        //接受用户输入并处理
	public void recieve() {
		try {
                        //获取用户控制台的输入输出流
			out = sk.getOutputStream();
			in = sk.getInputStream();
			out.write("welcome!Please enter your username:\r\n".getBytes());
			bs = new BufferedReader(new InputStreamReader(in));
                        //获取用户输入，如果用户没有按回车，程序会在此暂停
			String name = bs.readLine();
			out.write(("Your username is:" + name + "\r\n").getBytes());
                        //初始化Message对象，并将用户的姓名、输出流、消息储存进用户的Message对象
			m = new Message(name, out, msg);
                        //用于区分用户的索引，比如第一个用户登录时这个值为0，第二个用户时这个值为2
			count = msg.size();
                        //将Message对象m存入Message队列的第count处
			msg.add(count, m);
                        //在所有用户的控制台输入XXX已经上线
			msg.get(count).setMsgAll(name + " is on line.\r\n");
                        //此函数会循环获取用户输入并在所有用户的控制台输出
			setMsg(bs);
                        //跳出上面的函数说明用户已经下线，则关闭所有的流、用户对应的Socket对象，从Message队列移除上线的用户
			out.close();
			in.close();
			sk.close();
			msg.remove(count);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

  </td></tr></table></body></html>